Compare commits
19 Commits
feature/ht
...
v4.0.1
Author | SHA1 | Date | |
---|---|---|---|
61e5f52286 | |||
ce0b716f54 | |||
aae8e5ec65 | |||
2723e8466e | |||
f13c610352 | |||
55c65b08bf | |||
a11aa3e0dd | |||
de0bf5ebcd | |||
15369e1ae9 | |||
d4115880b9 | |||
3c80c75e4a | |||
5cb72dce4c | |||
d2747487e3 | |||
12e664fc61 | |||
cbf21b4008 | |||
68c1bf7017 | |||
257c901255 | |||
15d8c663da | |||
d50125c62d |
28
.travis.yml
28
.travis.yml
@ -12,23 +12,23 @@ matrix:
|
||||
- python test/run.py
|
||||
- make ws
|
||||
|
||||
# # Linux
|
||||
# - os: linux
|
||||
# dist: xenial
|
||||
# script:
|
||||
# - python test/run.py
|
||||
# - make ws
|
||||
# env:
|
||||
# - CC=gcc
|
||||
# - CXX=g++
|
||||
|
||||
# Clang + Linux disabled for now
|
||||
# Linux
|
||||
- os: linux
|
||||
dist: xenial
|
||||
script: python test/run.py
|
||||
script:
|
||||
- python test/run.py
|
||||
- make ws
|
||||
env:
|
||||
- CC=clang
|
||||
- CXX=clang++
|
||||
- CC=gcc
|
||||
- CXX=g++
|
||||
|
||||
# Clang + Linux disabled for now
|
||||
# - os: linux
|
||||
# dist: xenial
|
||||
# script: python test/run.py
|
||||
# env:
|
||||
# - CC=clang
|
||||
# - CXX=clang++
|
||||
|
||||
# Windows
|
||||
- os: windows
|
||||
|
30
CHANGELOG.md
Normal file
30
CHANGELOG.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [unreleased] - 2019-06-09
|
||||
### Changed
|
||||
- travis CI uses g++ on Linux
|
||||
|
||||
## [4.0.0] - 2019-06-09
|
||||
### Changed
|
||||
- WebSocket::send() sends message in TEXT mode by default
|
||||
- WebSocketMessage sets a new binary field, which tells whether the received incoming message is binary or text
|
||||
- WebSocket::send takes a third arg, binary which default to true (can be text too)
|
||||
- WebSocket callback only take one object, a const ix::WebSocketMessagePtr& msg
|
||||
- Add explicit WebSocket::sendBinary method
|
||||
- New headers + WebSocketMessage class to hold message data, still not used across the board
|
||||
- Add test/compatibility folder with small servers and clients written in different languages and different libraries to test compatibility.
|
||||
- ws echo_server has a -g option to print a greeting message on connect
|
||||
- IXSocketMbedTLS: better error handling in close and connect
|
||||
|
||||
## [3.1.2] - 2019-06-06
|
||||
### Added
|
||||
- ws connect has a -x option to disable per message deflate
|
||||
- Add WebSocket::disablePerMessageDeflate() option.
|
||||
|
||||
## [3.0.0] - 2019-06-xx
|
||||
### Changed
|
||||
- TLS, aka SSL works on Windows (websocket and http clients)
|
||||
- ws command line tool build on Windows
|
||||
- Async API for HttpClient
|
||||
- HttpClient API changed to use shared_ptr for response and request
|
@ -61,6 +61,10 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXWebSocketHandshake.h
|
||||
ixwebsocket/IXWebSocketSendInfo.h
|
||||
ixwebsocket/IXWebSocketErrorInfo.h
|
||||
ixwebsocket/IXWebSocketCloseInfo.h
|
||||
ixwebsocket/IXWebSocketOpenInfo.h
|
||||
ixwebsocket/IXWebSocketMessageType.h
|
||||
ixwebsocket/IXWebSocketMessage.h
|
||||
ixwebsocket/IXWebSocketPerMessageDeflate.h
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateCodec.h
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.h
|
||||
|
@ -1 +1 @@
|
||||
2.2.1
|
||||
4.0.0
|
||||
|
@ -1 +1 @@
|
||||
docker/Dockerfile.ubuntu_artful
|
||||
docker/Dockerfile.alpine
|
141
README.md
141
README.md
@ -28,29 +28,27 @@ webSocket.setUrl(url);
|
||||
// to make sure that load balancers do not kill an idle connection.
|
||||
webSocket.setHeartBeatPeriod(45);
|
||||
|
||||
// Per message deflate connection is enabled by default. You can tweak its parameters or disable it
|
||||
webSocket.disablePerMessageDeflate();
|
||||
|
||||
// Setup a callback to be fired when a message or an event (open, close, error) is received
|
||||
webSocket.setOnMessageCallback(
|
||||
[](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Message)
|
||||
if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
std::cout << str << std::endl;
|
||||
std::cout << msg->str << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
// Now that our callback is setup, we can start our background thread and receive messages
|
||||
webSocket.start();
|
||||
|
||||
// Send a message to the server (default to BINARY mode)
|
||||
// Send a message to the server (default to TEXT mode)
|
||||
webSocket.send("hello world");
|
||||
|
||||
// The message can be sent in TEXT mode
|
||||
webSocket.sendText("hello again");
|
||||
// The message can be sent in BINARY mode (useful if you send MsgPack data for example)
|
||||
webSocket.sendBinary("some serialized binary data");
|
||||
|
||||
// ... finally ...
|
||||
|
||||
@ -70,14 +68,9 @@ server.setOnConnectionCallback(
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cerr << "New connection" << std::endl;
|
||||
|
||||
@ -88,19 +81,21 @@ server.setOnConnectionCallback(
|
||||
std::cerr << "id: " << connectionState->getId() << std::endl;
|
||||
|
||||
// The uri the client did connect to.
|
||||
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
|
||||
std::cerr << "Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
// For an echo server, we just send back to the client whatever was received by the server
|
||||
// All connected clients are available in an std::set. See the broadcast cpp example.
|
||||
webSocket->send(str);
|
||||
// Second parameter tells whether we are sending the message in binary or text mode.
|
||||
// Here we send it in the same mode as it was received.
|
||||
webSocket->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -129,33 +124,33 @@ Here is what the HTTP client API looks like. Note that HTTP client support is ve
|
||||
// Preparation
|
||||
//
|
||||
HttpClient httpClient;
|
||||
HttpRequestArgs args;
|
||||
HttpRequestArgsPtr args = httpClient.createRequest();
|
||||
|
||||
// Custom headers can be set
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["Foo"] = "bar";
|
||||
args.extraHeaders = headers;
|
||||
args->extraHeaders = headers;
|
||||
|
||||
// Timeout options
|
||||
args.connectTimeout = connectTimeout;
|
||||
args.transferTimeout = transferTimeout;
|
||||
args->connectTimeout = connectTimeout;
|
||||
args->transferTimeout = transferTimeout;
|
||||
|
||||
// Redirect options
|
||||
args.followRedirects = followRedirects;
|
||||
args.maxRedirects = maxRedirects;
|
||||
args->followRedirects = followRedirects;
|
||||
args->maxRedirects = maxRedirects;
|
||||
|
||||
// Misc
|
||||
args.compress = compress; // Enable gzip compression
|
||||
args.verbose = verbose;
|
||||
args.logger = [](const std::string& msg)
|
||||
args->compress = compress; // Enable gzip compression
|
||||
args->verbose = verbose;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
};
|
||||
|
||||
//
|
||||
// Request
|
||||
// Synchronous Request
|
||||
//
|
||||
HttpResponse out;
|
||||
HttpResponsePtr out;
|
||||
std::string url = "https://www.google.com";
|
||||
|
||||
// HEAD request
|
||||
@ -175,13 +170,30 @@ out = httpClient.post(url, std::string("foo=bar"), args);
|
||||
//
|
||||
// Result
|
||||
//
|
||||
auto statusCode = std::get<0>(out);
|
||||
auto errorCode = std::get<1>(out);
|
||||
auto responseHeaders = std::get<2>(out);
|
||||
auto payload = std::get<3>(out);
|
||||
auto errorMsg = std::get<4>(out);
|
||||
auto uploadSize = std::get<5>(out);
|
||||
auto downloadSize = std::get<6>(out);
|
||||
auto errorCode = response->errorCode; // Can be HttpErrorCode::Ok, HttpErrorCode::UrlMalformed, etc...
|
||||
auto errorCode = response->errorCode; // 200, 404, etc...
|
||||
auto responseHeaders = response->headers; // All the headers in a special case-insensitive unordered_map of (string, string)
|
||||
auto payload = response->payload; // All the bytes from the response as an std::string
|
||||
auto errorMsg = response->errorMsg; // Descriptive error message in case of failure
|
||||
auto uploadSize = response->uploadSize; // Byte count of uploaded data
|
||||
auto downloadSize = response->downloadSize; // Byte count of downloaded data
|
||||
|
||||
//
|
||||
// Asynchronous Request
|
||||
//
|
||||
bool async = true;
|
||||
HttpClient httpClient(async);
|
||||
auto args = httpClient.createRequest(url, HttpClient::kGet);
|
||||
|
||||
// Push the request to a queue,
|
||||
bool ok = httpClient.performRequest(args, [](const HttpResponsePtr& response)
|
||||
{
|
||||
// This callback execute in a background thread. Make sure you uses appropriate protection such as mutex
|
||||
auto statusCode = response->statusCode; // acess results
|
||||
}
|
||||
);
|
||||
|
||||
// ok will be false if your httpClient is not async
|
||||
```
|
||||
|
||||
## Build
|
||||
@ -238,7 +250,7 @@ No manual polling to fetch data is required. Data is sent and received instantly
|
||||
|
||||
### Automatic reconnection
|
||||
|
||||
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
|
||||
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds. This behavior can be disabled.
|
||||
|
||||
### Large messages
|
||||
|
||||
@ -314,32 +326,27 @@ The onMessage event will be fired when the connection is opened or closed. This
|
||||
|
||||
```
|
||||
webSocket.setOnMessageCallback(
|
||||
[](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cout << "send greetings" << std::endl;
|
||||
|
||||
// Headers can be inspected (pairs of string/string)
|
||||
std::cout << "Handshake Headers:" << std::endl;
|
||||
for (auto it : headers)
|
||||
for (auto it : msg->headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::cout << "disconnected" << std::endl;
|
||||
|
||||
// The server can send an explicit code and reason for closing.
|
||||
// This data can be accessed through the closeInfo object.
|
||||
std::cout << closeInfo.code << std::endl;
|
||||
std::cout << closeInfo.reason << std::endl;
|
||||
std::cout << msg->closeInfo.code << std::endl;
|
||||
std::cout << msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -351,20 +358,15 @@ A message will be fired when there is an error with the connection. The message
|
||||
|
||||
```
|
||||
webSocket.setOnMessageCallback(
|
||||
[](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Error)
|
||||
if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Error: " << error.reason << std::endl;
|
||||
ss << "#retries: " << event.retries << std::endl;
|
||||
ss << "Wait time(ms): " << event.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << event.http_status << std::endl;
|
||||
ss << "Error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->eventInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->eventInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->eventInfo.http_status << std::endl;
|
||||
std::cout << ss.str() << std::endl;
|
||||
}
|
||||
}
|
||||
@ -391,17 +393,12 @@ Ping/pong messages are used to implement keep-alive. 2 message types exists to i
|
||||
|
||||
```
|
||||
webSocket.setOnMessageCallback(
|
||||
[](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Ping ||
|
||||
messageType == ix::WebSocketMessageType::Pong)
|
||||
if (msg->type == ix::WebSocketMessageType::Ping ||
|
||||
msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
std::cout << "pong data: " << str << std::endl;
|
||||
std::cout << "pong data: " << msg->str << std::endl;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
23
docker/Dockerfile.ubuntu_bionic
Normal file
23
docker/Dockerfile.ubuntu_bionic
Normal file
@ -0,0 +1,23 @@
|
||||
# Build time
|
||||
FROM ubuntu:bionic as build
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN apt-get update
|
||||
RUN apt-get -y install wget
|
||||
RUN mkdir -p /tmp/cmake
|
||||
WORKDIR /tmp/cmake
|
||||
RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.0/cmake-3.14.0-Linux-x86_64.tar.gz
|
||||
RUN tar zxf cmake-3.14.0-Linux-x86_64.tar.gz
|
||||
|
||||
RUN apt-get -y install g++
|
||||
RUN apt-get -y install libssl-dev
|
||||
RUN apt-get -y install libz-dev
|
||||
RUN apt-get -y install make
|
||||
RUN apt-get -y install python
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG CMAKE_BIN_PATH=/tmp/cmake/cmake-3.14.0-Linux-x86_64/bin
|
||||
ENV PATH="${CMAKE_BIN_PATH}:${PATH}"
|
||||
|
||||
RUN ["make", "ws"]
|
@ -24,23 +24,95 @@ namespace ix
|
||||
const std::string HttpClient::kDel = "DEL";
|
||||
const std::string HttpClient::kPut = "PUT";
|
||||
|
||||
HttpClient::HttpClient()
|
||||
HttpClient::HttpClient(bool async) : _async(async), _stop(false)
|
||||
{
|
||||
if (!_async) return;
|
||||
|
||||
_thread = std::thread(&HttpClient::run, this);
|
||||
}
|
||||
|
||||
HttpClient::~HttpClient()
|
||||
{
|
||||
if (!_thread.joinable()) return;
|
||||
|
||||
_stop = true;
|
||||
_condition.notify_one();
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::request(
|
||||
HttpRequestArgsPtr HttpClient::createRequest(const std::string& url,
|
||||
const std::string& verb)
|
||||
{
|
||||
auto request = std::make_shared<HttpRequestArgs>();
|
||||
request->url = url;
|
||||
request->verb = verb;
|
||||
return request;
|
||||
}
|
||||
|
||||
bool HttpClient::performRequest(HttpRequestArgsPtr args,
|
||||
const OnResponseCallback& onResponseCallback)
|
||||
{
|
||||
if (!_async) return false;
|
||||
|
||||
// Enqueue the task
|
||||
{
|
||||
// acquire lock
|
||||
std::unique_lock<std::mutex> lock(_queueMutex);
|
||||
|
||||
// add the task
|
||||
_queue.push(std::make_pair(args, onResponseCallback));
|
||||
} // release lock
|
||||
|
||||
// wake up one thread
|
||||
_condition.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpClient::run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
HttpRequestArgsPtr args;
|
||||
OnResponseCallback onResponseCallback;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_queueMutex);
|
||||
|
||||
while (!_stop && _queue.empty())
|
||||
{
|
||||
_condition.wait(lock);
|
||||
}
|
||||
|
||||
if (_stop) return;
|
||||
|
||||
auto p = _queue.front();
|
||||
_queue.pop();
|
||||
|
||||
args = p.first;
|
||||
onResponseCallback = p.second;
|
||||
}
|
||||
|
||||
if (_stop) return;
|
||||
|
||||
HttpResponsePtr response = request(args->url, args->verb, args->body, args);
|
||||
onResponseCallback(response);
|
||||
|
||||
if (_stop) return;
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpClient::request(
|
||||
const std::string& url,
|
||||
const std::string& verb,
|
||||
const std::string& body,
|
||||
const HttpRequestArgs& args,
|
||||
HttpRequestArgsPtr args,
|
||||
int redirects)
|
||||
{
|
||||
// We only have one socket connection, so we cannot
|
||||
// make multiple requests concurrently.
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
uint64_t uploadSize = 0;
|
||||
uint64_t downloadSize = 0;
|
||||
int code = 0;
|
||||
@ -54,7 +126,7 @@ namespace ix
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cannot parse url: " << url;
|
||||
return HttpResponse(code, HttpErrorCode::UrlMalformed,
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::UrlMalformed,
|
||||
headers, payload, ss.str(),
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
@ -65,7 +137,7 @@ namespace ix
|
||||
|
||||
if (!_socket)
|
||||
{
|
||||
return HttpResponse(code, HttpErrorCode::CannotCreateSocket,
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotCreateSocket,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
@ -75,13 +147,13 @@ namespace ix
|
||||
ss << verb << " " << path << " HTTP/1.1\r\n";
|
||||
ss << "Host: " << host << "\r\n";
|
||||
|
||||
if (args.compress)
|
||||
if (args->compress)
|
||||
{
|
||||
ss << "Accept-Encoding: gzip" << "\r\n";
|
||||
}
|
||||
|
||||
// Append extra headers
|
||||
for (auto&& it : args.extraHeaders)
|
||||
for (auto&& it : args->extraHeaders)
|
||||
{
|
||||
ss << it.first << ": " << it.second << "\r\n";
|
||||
}
|
||||
@ -103,7 +175,7 @@ namespace ix
|
||||
ss << "Content-Length: " << body.size() << "\r\n";
|
||||
|
||||
// Set default Content-Type if unspecified
|
||||
if (args.extraHeaders.find("Content-Type") == args.extraHeaders.end())
|
||||
if (args->extraHeaders.find("Content-Type") == args->extraHeaders.end())
|
||||
{
|
||||
ss << "Content-Type: application/x-www-form-urlencoded" << "\r\n";
|
||||
}
|
||||
@ -121,23 +193,23 @@ namespace ix
|
||||
|
||||
// Make a cancellation object dealing with connection timeout
|
||||
auto isCancellationRequested =
|
||||
makeCancellationRequestWithTimeout(args.connectTimeout, requestInitCancellation);
|
||||
makeCancellationRequestWithTimeout(args->connectTimeout, requestInitCancellation);
|
||||
|
||||
bool success = _socket->connect(host, port, errMsg, isCancellationRequested);
|
||||
if (!success)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cannot connect to url: " << url << " / error : " << errMsg;
|
||||
return HttpResponse(code, HttpErrorCode::CannotConnect,
|
||||
headers, payload, ss.str(),
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotConnect,
|
||||
headers, payload, ss.str(),
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
// Make a new cancellation object dealing with transfer timeout
|
||||
isCancellationRequested =
|
||||
makeCancellationRequestWithTimeout(args.transferTimeout, requestInitCancellation);
|
||||
makeCancellationRequestWithTimeout(args->transferTimeout, requestInitCancellation);
|
||||
|
||||
if (args.verbose)
|
||||
if (args->verbose)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Sending " << verb << " request "
|
||||
@ -154,9 +226,9 @@ namespace ix
|
||||
if (!_socket->writeBytes(req, isCancellationRequested))
|
||||
{
|
||||
std::string errorMsg("Cannot send request");
|
||||
return HttpResponse(code, HttpErrorCode::SendError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::SendError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
uploadSize = req.size();
|
||||
@ -168,12 +240,12 @@ namespace ix
|
||||
if (!lineValid)
|
||||
{
|
||||
std::string errorMsg("Cannot retrieve status line");
|
||||
return HttpResponse(code, HttpErrorCode::CannotReadStatusLine,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotReadStatusLine,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
if (args.verbose)
|
||||
if (args->verbose)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Status line " << line;
|
||||
@ -183,9 +255,9 @@ namespace ix
|
||||
if (sscanf(line.c_str(), "HTTP/1.1 %d", &code) != 1)
|
||||
{
|
||||
std::string errorMsg("Cannot parse response code from status line");
|
||||
return HttpResponse(code, HttpErrorCode::MissingStatus,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::MissingStatus,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
auto result = parseHttpHeaders(_socket, isCancellationRequested);
|
||||
@ -195,29 +267,29 @@ namespace ix
|
||||
if (!headersValid)
|
||||
{
|
||||
std::string errorMsg("Cannot parse http headers");
|
||||
return HttpResponse(code, HttpErrorCode::HeaderParsingError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::HeaderParsingError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
// Redirect ?
|
||||
if ((code >= 301 && code <= 308) && args.followRedirects)
|
||||
if ((code >= 301 && code <= 308) && args->followRedirects)
|
||||
{
|
||||
if (headers.find("Location") == headers.end())
|
||||
{
|
||||
std::string errorMsg("Missing location header for redirect");
|
||||
return HttpResponse(code, HttpErrorCode::MissingLocation,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::MissingLocation,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
if (redirects >= args.maxRedirects)
|
||||
if (redirects >= args->maxRedirects)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Too many redirects: " << redirects;
|
||||
return HttpResponse(code, HttpErrorCode::TooManyRedirects,
|
||||
headers, payload, ss.str(),
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::TooManyRedirects,
|
||||
headers, payload, ss.str(),
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
// Recurse
|
||||
@ -227,9 +299,9 @@ namespace ix
|
||||
|
||||
if (verb == "HEAD")
|
||||
{
|
||||
return HttpResponse(code, HttpErrorCode::Ok,
|
||||
headers, payload, std::string(),
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::Ok,
|
||||
headers, payload, std::string(),
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
// Parse response:
|
||||
@ -243,14 +315,14 @@ namespace ix
|
||||
payload.reserve(contentLength);
|
||||
|
||||
auto chunkResult = _socket->readBytes(contentLength,
|
||||
args.onProgressCallback,
|
||||
args->onProgressCallback,
|
||||
isCancellationRequested);
|
||||
if (!chunkResult.first)
|
||||
{
|
||||
errorMsg = "Cannot read chunk";
|
||||
return HttpResponse(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
payload += chunkResult.second;
|
||||
}
|
||||
@ -266,9 +338,9 @@ namespace ix
|
||||
|
||||
if (!lineResult.first)
|
||||
{
|
||||
return HttpResponse(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
uint64_t chunkSize;
|
||||
@ -276,7 +348,7 @@ namespace ix
|
||||
ss << std::hex << line;
|
||||
ss >> chunkSize;
|
||||
|
||||
if (args.verbose)
|
||||
if (args->verbose)
|
||||
{
|
||||
std::stringstream oss;
|
||||
oss << "Reading " << chunkSize << " bytes"
|
||||
@ -288,14 +360,14 @@ namespace ix
|
||||
|
||||
// Read a chunk
|
||||
auto chunkResult = _socket->readBytes((size_t) chunkSize,
|
||||
args.onProgressCallback,
|
||||
args->onProgressCallback,
|
||||
isCancellationRequested);
|
||||
if (!chunkResult.first)
|
||||
{
|
||||
errorMsg = "Cannot read chunk";
|
||||
return HttpResponse(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
payload += chunkResult.second;
|
||||
|
||||
@ -304,9 +376,9 @@ namespace ix
|
||||
|
||||
if (!lineResult.first)
|
||||
{
|
||||
return HttpResponse(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::ChunkReadError,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
if (chunkSize == 0) break;
|
||||
@ -319,9 +391,9 @@ namespace ix
|
||||
else
|
||||
{
|
||||
std::string errorMsg("Cannot read http body");
|
||||
return HttpResponse(code, HttpErrorCode::CannotReadBody,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::CannotReadBody,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
downloadSize = payload.size();
|
||||
@ -333,60 +405,60 @@ namespace ix
|
||||
if (!gzipInflate(payload, decompressedPayload))
|
||||
{
|
||||
std::string errorMsg("Error decompressing payload");
|
||||
return HttpResponse(code, HttpErrorCode::Gzip,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::Gzip,
|
||||
headers, payload, errorMsg,
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
payload = decompressedPayload;
|
||||
}
|
||||
|
||||
return HttpResponse(code, HttpErrorCode::Ok,
|
||||
headers, payload, std::string(),
|
||||
uploadSize, downloadSize);
|
||||
return std::make_shared<HttpResponse>(code, HttpErrorCode::Ok,
|
||||
headers, payload, std::string(),
|
||||
uploadSize, downloadSize);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::get(const std::string& url,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::get(const std::string& url,
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kGet, std::string(), args);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::head(const std::string& url,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::head(const std::string& url,
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kHead, std::string(), args);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::del(const std::string& url,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::del(const std::string& url,
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kDel, std::string(), args);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::post(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::post(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kPost, serializeHttpParameters(httpParameters), args);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::post(const std::string& url,
|
||||
const std::string& body,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::post(const std::string& url,
|
||||
const std::string& body,
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kPost, body, args);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::put(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::put(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kPut, serializeHttpParameters(httpParameters), args);
|
||||
}
|
||||
|
||||
HttpResponse HttpClient::put(const std::string& url,
|
||||
const std::string& body,
|
||||
const HttpRequestArgs& args)
|
||||
HttpResponsePtr HttpClient::put(const std::string& url,
|
||||
const std::string& body,
|
||||
const HttpRequestArgsPtr args)
|
||||
{
|
||||
return request(url, kPut, body, args);
|
||||
}
|
||||
@ -488,11 +560,11 @@ namespace ix
|
||||
}
|
||||
|
||||
void HttpClient::log(const std::string& msg,
|
||||
const HttpRequestArgs& args)
|
||||
HttpRequestArgsPtr args)
|
||||
{
|
||||
if (args.logger)
|
||||
if (args->logger)
|
||||
{
|
||||
args.logger(msg);
|
||||
args->logger(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,11 +10,13 @@
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@ -34,7 +36,8 @@ namespace ix
|
||||
MissingLocation = 11,
|
||||
TooManyRedirects = 12,
|
||||
ChunkReadError = 13,
|
||||
CannotReadBody = 14
|
||||
CannotReadBody = 14,
|
||||
Invalid = 100
|
||||
};
|
||||
|
||||
struct HttpResponse
|
||||
@ -66,12 +69,15 @@ namespace ix
|
||||
}
|
||||
};
|
||||
|
||||
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
|
||||
using HttpParameters = std::map<std::string, std::string>;
|
||||
using Logger = std::function<void(const std::string&)>;
|
||||
using OnResponseCallback = std::function<void(const HttpResponsePtr&)>;
|
||||
|
||||
struct HttpRequestArgs
|
||||
{
|
||||
std::string url;
|
||||
std::string verb;
|
||||
WebSocketHttpHeaders extraHeaders;
|
||||
std::string body;
|
||||
int connectTimeout;
|
||||
@ -84,51 +90,72 @@ namespace ix
|
||||
OnProgressCallback onProgressCallback;
|
||||
};
|
||||
|
||||
using HttpRequestArgsPtr = std::shared_ptr<HttpRequestArgs>;
|
||||
|
||||
class HttpClient
|
||||
{
|
||||
public:
|
||||
HttpClient();
|
||||
HttpClient(bool async = false);
|
||||
~HttpClient();
|
||||
|
||||
HttpResponse get(const std::string& url, const HttpRequestArgs& args);
|
||||
HttpResponse head(const std::string& url, const HttpRequestArgs& args);
|
||||
HttpResponse del(const std::string& url, const HttpRequestArgs& args);
|
||||
HttpResponsePtr get(const std::string& url, HttpRequestArgsPtr args);
|
||||
HttpResponsePtr head(const std::string& url, HttpRequestArgsPtr args);
|
||||
HttpResponsePtr del(const std::string& url, HttpRequestArgsPtr args);
|
||||
|
||||
HttpResponse post(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
const HttpRequestArgs& args);
|
||||
HttpResponse post(const std::string& url,
|
||||
const std::string& body,
|
||||
const HttpRequestArgs& args);
|
||||
|
||||
HttpResponse put(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
const HttpRequestArgs& args);
|
||||
HttpResponse put(const std::string& url,
|
||||
const std::string& body,
|
||||
const HttpRequestArgs& args);
|
||||
|
||||
HttpResponse request(const std::string& url,
|
||||
const std::string& verb,
|
||||
HttpResponsePtr post(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
HttpRequestArgsPtr args);
|
||||
HttpResponsePtr post(const std::string& url,
|
||||
const std::string& body,
|
||||
const HttpRequestArgs& args,
|
||||
int redirects = 0);
|
||||
HttpRequestArgsPtr args);
|
||||
|
||||
HttpResponsePtr put(const std::string& url,
|
||||
const HttpParameters& httpParameters,
|
||||
HttpRequestArgsPtr args);
|
||||
HttpResponsePtr put(const std::string& url,
|
||||
const std::string& body,
|
||||
HttpRequestArgsPtr args);
|
||||
|
||||
HttpResponsePtr request(const std::string& url,
|
||||
const std::string& verb,
|
||||
const std::string& body,
|
||||
HttpRequestArgsPtr args,
|
||||
int redirects = 0);
|
||||
|
||||
// Async API
|
||||
HttpRequestArgsPtr createRequest(const std::string& url = std::string(),
|
||||
const std::string& verb = HttpClient::kGet);
|
||||
|
||||
bool performRequest(HttpRequestArgsPtr request,
|
||||
const OnResponseCallback& onResponseCallback);
|
||||
|
||||
std::string serializeHttpParameters(const HttpParameters& httpParameters);
|
||||
|
||||
std::string urlEncode(const std::string& value);
|
||||
|
||||
private:
|
||||
void log(const std::string& msg, const HttpRequestArgs& args);
|
||||
|
||||
bool gzipInflate(const std::string& in, std::string& out);
|
||||
|
||||
std::shared_ptr<Socket> _socket;
|
||||
|
||||
const static std::string kPost;
|
||||
const static std::string kGet;
|
||||
const static std::string kHead;
|
||||
const static std::string kDel;
|
||||
const static std::string kPut;
|
||||
|
||||
private:
|
||||
void log(const std::string& msg, HttpRequestArgsPtr args);
|
||||
|
||||
bool gzipInflate(const std::string& in, std::string& out);
|
||||
|
||||
// Async API background thread runner
|
||||
void run();
|
||||
|
||||
// Async API
|
||||
bool _async;
|
||||
std::queue<std::pair<HttpRequestArgsPtr, OnResponseCallback>> _queue;
|
||||
mutable std::mutex _queueMutex;
|
||||
std::condition_variable _condition;
|
||||
std::atomic<bool> _stop;
|
||||
std::thread _thread;
|
||||
|
||||
std::shared_ptr<Socket> _socket;
|
||||
std::mutex _mutex; // to protect accessing the _socket (only one socket per client)
|
||||
};
|
||||
} // namespace ix
|
||||
|
@ -24,6 +24,8 @@ namespace ix
|
||||
|
||||
bool SocketMbedTLS::init(const std::string& host, std::string& errMsg)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
mbedtls_ssl_init(&_ssl);
|
||||
mbedtls_ssl_config_init(&_conf);
|
||||
mbedtls_ctr_drbg_init(&_ctr_drbg);
|
||||
@ -75,15 +77,24 @@ namespace ix
|
||||
std::string& errMsg,
|
||||
const CancellationRequest& isCancellationRequested)
|
||||
{
|
||||
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
|
||||
if (_sockfd == -1) return false;
|
||||
if (!init(host, errMsg)) return false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
|
||||
if (_sockfd == -1) return false;
|
||||
}
|
||||
|
||||
if (!init(host, errMsg))
|
||||
{
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
|
||||
int res;
|
||||
do
|
||||
do
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
res = mbedtls_ssl_handshake(&_ssl);
|
||||
}
|
||||
while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
|
||||
@ -95,6 +106,8 @@ namespace ix
|
||||
|
||||
errMsg = "error in handshake : ";
|
||||
errMsg += buf;
|
||||
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -103,10 +116,14 @@ namespace ix
|
||||
|
||||
void SocketMbedTLS::close()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
mbedtls_ssl_free(&_ssl);
|
||||
mbedtls_ssl_config_free(&_conf);
|
||||
mbedtls_ctr_drbg_free(&_ctr_drbg);
|
||||
mbedtls_entropy_free(&_entropy);
|
||||
|
||||
Socket::close();
|
||||
}
|
||||
|
||||
ssize_t SocketMbedTLS::send(char* buf, size_t nbyte)
|
||||
|
@ -51,9 +51,11 @@ namespace ix
|
||||
_ws.setOnCloseCallback(
|
||||
[this](uint16_t code, const std::string& reason, size_t wireSize, bool remote)
|
||||
{
|
||||
_onMessageCallback(WebSocketMessageType::Close, "", wireSize,
|
||||
WebSocketErrorInfo(), WebSocketOpenInfo(),
|
||||
WebSocketCloseInfo(code, reason, remote));
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(
|
||||
WebSocketMessageType::Close, "", wireSize,
|
||||
WebSocketErrorInfo(), WebSocketOpenInfo(),
|
||||
WebSocketCloseInfo(code, reason, remote)));
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -135,6 +137,13 @@ namespace ix
|
||||
_enablePong = false;
|
||||
}
|
||||
|
||||
void WebSocket::disablePerMessageDeflate()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_configMutex);
|
||||
WebSocketPerMessageDeflateOptions perMessageDeflateOptions(false);
|
||||
_perMessageDeflateOptions = perMessageDeflateOptions;
|
||||
}
|
||||
|
||||
void WebSocket::start()
|
||||
{
|
||||
if (_thread.joinable()) return; // we've already been started
|
||||
@ -173,10 +182,12 @@ namespace ix
|
||||
return status;
|
||||
}
|
||||
|
||||
_onMessageCallback(WebSocketMessageType::Open, "", 0,
|
||||
WebSocketErrorInfo(),
|
||||
WebSocketOpenInfo(status.uri, status.headers),
|
||||
WebSocketCloseInfo());
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(
|
||||
WebSocketMessageType::Open, "", 0,
|
||||
WebSocketErrorInfo(),
|
||||
WebSocketOpenInfo(status.uri, status.headers),
|
||||
WebSocketCloseInfo()));
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -196,10 +207,12 @@ namespace ix
|
||||
return status;
|
||||
}
|
||||
|
||||
_onMessageCallback(WebSocketMessageType::Open, "", 0,
|
||||
WebSocketErrorInfo(),
|
||||
WebSocketOpenInfo(status.uri, status.headers),
|
||||
WebSocketCloseInfo());
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(
|
||||
WebSocketMessageType::Open, "", 0,
|
||||
WebSocketErrorInfo(),
|
||||
WebSocketOpenInfo(status.uri, status.headers),
|
||||
WebSocketCloseInfo()));
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -267,9 +280,11 @@ namespace ix
|
||||
connectErr.reason = status.errorStr;
|
||||
connectErr.http_status = status.http_status;
|
||||
|
||||
_onMessageCallback(WebSocketMessageType::Error, "", 0,
|
||||
connectErr, WebSocketOpenInfo(),
|
||||
WebSocketCloseInfo());
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(
|
||||
WebSocketMessageType::Error, "", 0,
|
||||
connectErr, WebSocketOpenInfo(),
|
||||
WebSocketCloseInfo()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,8 +325,8 @@ namespace ix
|
||||
WebSocketMessageType webSocketMessageType;
|
||||
switch (messageKind)
|
||||
{
|
||||
default:
|
||||
case WebSocketTransport::MessageKind::MSG:
|
||||
case WebSocketTransport::MessageKind::MSG_TEXT:
|
||||
case WebSocketTransport::MessageKind::MSG_BINARY:
|
||||
{
|
||||
webSocketMessageType = WebSocketMessageType::Message;
|
||||
} break;
|
||||
@ -335,9 +350,13 @@ namespace ix
|
||||
WebSocketErrorInfo webSocketErrorInfo;
|
||||
webSocketErrorInfo.decompressionError = decompressionError;
|
||||
|
||||
_onMessageCallback(webSocketMessageType, msg, wireSize,
|
||||
webSocketErrorInfo, WebSocketOpenInfo(),
|
||||
WebSocketCloseInfo());
|
||||
bool binary = messageKind == WebSocketTransport::MessageKind::MSG_BINARY;
|
||||
|
||||
_onMessageCallback(
|
||||
std::make_shared<WebSocketMessage>(
|
||||
webSocketMessageType, msg, wireSize,
|
||||
webSocketErrorInfo, WebSocketOpenInfo(),
|
||||
WebSocketCloseInfo(), binary));
|
||||
|
||||
WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
|
||||
});
|
||||
@ -368,9 +387,18 @@ namespace ix
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocket::send(const std::string& data,
|
||||
bool binary,
|
||||
const OnProgressCallback& onProgressCallback)
|
||||
{
|
||||
return sendMessage(data, SendMessageKind::Binary, onProgressCallback);
|
||||
return sendMessage(data,
|
||||
(binary) ? SendMessageKind::Binary: SendMessageKind::Text,
|
||||
onProgressCallback);
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocket::sendBinary(const std::string& text,
|
||||
const OnProgressCallback& onProgressCallback)
|
||||
{
|
||||
return sendMessage(text, SendMessageKind::Binary, onProgressCallback);
|
||||
}
|
||||
|
||||
WebSocketSendInfo WebSocket::sendText(const std::string& text,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "IXWebSocketCloseConstants.h"
|
||||
#include "IXWebSocketErrorInfo.h"
|
||||
#include "IXWebSocketHttpHeaders.h"
|
||||
#include "IXWebSocketMessage.h"
|
||||
#include "IXWebSocketPerMessageDeflateOptions.h"
|
||||
#include "IXWebSocketSendInfo.h"
|
||||
#include "IXWebSocketTransport.h"
|
||||
@ -32,52 +33,7 @@ namespace ix
|
||||
Closed = 3
|
||||
};
|
||||
|
||||
enum class WebSocketMessageType
|
||||
{
|
||||
Message = 0,
|
||||
Open = 1,
|
||||
Close = 2,
|
||||
Error = 3,
|
||||
Ping = 4,
|
||||
Pong = 5,
|
||||
Fragment = 6
|
||||
};
|
||||
|
||||
struct WebSocketOpenInfo
|
||||
{
|
||||
std::string uri;
|
||||
WebSocketHttpHeaders headers;
|
||||
|
||||
WebSocketOpenInfo(const std::string& u = std::string(),
|
||||
const WebSocketHttpHeaders& h = WebSocketHttpHeaders())
|
||||
: uri(u)
|
||||
, headers(h)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
struct WebSocketCloseInfo
|
||||
{
|
||||
uint16_t code;
|
||||
std::string reason;
|
||||
bool remote;
|
||||
|
||||
WebSocketCloseInfo(uint16_t c = 0, const std::string& r = std::string(), bool rem = false)
|
||||
: code(c)
|
||||
, reason(r)
|
||||
, remote(rem)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
using OnMessageCallback = std::function<void(WebSocketMessageType,
|
||||
const std::string&,
|
||||
size_t wireSize,
|
||||
const WebSocketErrorInfo&,
|
||||
const WebSocketOpenInfo&,
|
||||
const WebSocketCloseInfo&)>;
|
||||
using OnMessageCallback = std::function<void(const WebSocketMessagePtr&)>;
|
||||
|
||||
using OnTrafficTrackerCallback = std::function<void(size_t size, bool incoming)>;
|
||||
|
||||
@ -95,6 +51,7 @@ namespace ix
|
||||
void setPingTimeout(int pingTimeoutSecs);
|
||||
void enablePong();
|
||||
void disablePong();
|
||||
void disablePerMessageDeflate();
|
||||
|
||||
// Run asynchronously, by calling start and stop.
|
||||
void start();
|
||||
@ -107,14 +64,18 @@ namespace ix
|
||||
WebSocketInitResult connect(int timeoutSecs);
|
||||
void run();
|
||||
|
||||
// send binary data
|
||||
// send is in binary mode by default
|
||||
WebSocketSendInfo send(const std::string& data,
|
||||
bool binary = false,
|
||||
const OnProgressCallback& onProgressCallback = nullptr);
|
||||
WebSocketSendInfo sendBinary(const std::string& text,
|
||||
const OnProgressCallback& onProgressCallback = nullptr);
|
||||
WebSocketSendInfo sendText(const std::string& text,
|
||||
const OnProgressCallback& onProgressCallback = nullptr);
|
||||
WebSocketSendInfo ping(const std::string& text);
|
||||
|
||||
void close(uint16_t code = 1000, const std::string& reason = "Normal closure");
|
||||
void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode,
|
||||
const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage);
|
||||
|
||||
void setOnMessageCallback(const OnMessageCallback& callback);
|
||||
static void setTrafficTrackerCallback(const OnTrafficTrackerCallback& callback);
|
||||
@ -168,7 +129,7 @@ namespace ix
|
||||
bool _enablePong;
|
||||
static const bool kDefaultEnablePong;
|
||||
|
||||
// Optional ping and ping timeout
|
||||
// Optional ping and pong timeout
|
||||
int _pingIntervalSecs;
|
||||
int _pingTimeoutSecs;
|
||||
static const int kDefaultPingIntervalSecs;
|
||||
|
25
ixwebsocket/IXWebSocketCloseInfo.h
Normal file
25
ixwebsocket/IXWebSocketCloseInfo.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* IXWebSocketCloseInfo.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct WebSocketCloseInfo
|
||||
{
|
||||
uint16_t code;
|
||||
std::string reason;
|
||||
bool remote;
|
||||
|
||||
WebSocketCloseInfo(uint16_t c = 0, const std::string& r = std::string(), bool rem = false)
|
||||
: code(c)
|
||||
, reason(r)
|
||||
, remote(rem)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
} // namespace ix
|
49
ixwebsocket/IXWebSocketMessage.h
Normal file
49
ixwebsocket/IXWebSocketMessage.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* IXWebSocketMessage.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IXWebSocketCloseInfo.h"
|
||||
#include "IXWebSocketErrorInfo.h"
|
||||
#include "IXWebSocketMessageType.h"
|
||||
#include "IXWebSocketOpenInfo.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct WebSocketMessage
|
||||
{
|
||||
WebSocketMessageType type;
|
||||
std::string str;
|
||||
size_t wireSize;
|
||||
WebSocketErrorInfo errorInfo;
|
||||
WebSocketOpenInfo openInfo;
|
||||
WebSocketCloseInfo closeInfo;
|
||||
bool binary;
|
||||
|
||||
WebSocketMessage(WebSocketMessageType t,
|
||||
const std::string& s,
|
||||
size_t w,
|
||||
WebSocketErrorInfo e,
|
||||
WebSocketOpenInfo o,
|
||||
WebSocketCloseInfo c,
|
||||
bool b = false)
|
||||
: type(t)
|
||||
, str(std::move(s))
|
||||
, wireSize(w)
|
||||
, errorInfo(e)
|
||||
, openInfo(o)
|
||||
, closeInfo(c)
|
||||
, binary(b)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
using WebSocketMessagePtr = std::shared_ptr<WebSocketMessage>;
|
||||
} // namespace ix
|
@ -32,14 +32,7 @@ namespace ix
|
||||
if (_websocket)
|
||||
{
|
||||
// set dummy callback just to avoid crash
|
||||
_websocket->setOnMessageCallback([](
|
||||
WebSocketMessageType,
|
||||
const std::string&,
|
||||
size_t,
|
||||
const WebSocketErrorInfo&,
|
||||
const WebSocketOpenInfo&,
|
||||
const WebSocketCloseInfo&)
|
||||
{});
|
||||
_websocket->setOnMessageCallback([](const WebSocketMessagePtr&) {});
|
||||
}
|
||||
|
||||
_websocket = websocket;
|
||||
@ -47,27 +40,10 @@ namespace ix
|
||||
// bind new
|
||||
if (_websocket)
|
||||
{
|
||||
_websocket->setOnMessageCallback([this](
|
||||
WebSocketMessageType type,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const WebSocketErrorInfo& errorInfo,
|
||||
const WebSocketOpenInfo& openInfo,
|
||||
const WebSocketCloseInfo& closeInfo)
|
||||
_websocket->setOnMessageCallback([this](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
MessagePtr message(new Message());
|
||||
|
||||
message->type = type;
|
||||
message->str = str;
|
||||
message->wireSize = wireSize;
|
||||
message->errorInfo = errorInfo;
|
||||
message->openInfo = openInfo;
|
||||
message->closeInfo = closeInfo;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_messagesMutex);
|
||||
_messages.emplace_back(std::move(message));
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(_messagesMutex);
|
||||
_messages.emplace_back(std::move(msg));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -81,10 +57,10 @@ namespace ix
|
||||
{
|
||||
_onMessageUserCallback = std::move(callback);
|
||||
}
|
||||
|
||||
WebSocketMessageQueue::MessagePtr WebSocketMessageQueue::popMessage()
|
||||
|
||||
WebSocketMessagePtr WebSocketMessageQueue::popMessage()
|
||||
{
|
||||
MessagePtr message;
|
||||
WebSocketMessagePtr message;
|
||||
std::lock_guard<std::mutex> lock(_messagesMutex);
|
||||
|
||||
if (!_messages.empty())
|
||||
@ -101,19 +77,11 @@ namespace ix
|
||||
if (!_onMessageUserCallback)
|
||||
return;
|
||||
|
||||
MessagePtr message;
|
||||
WebSocketMessagePtr message;
|
||||
|
||||
while (count > 0 && (message = popMessage()))
|
||||
{
|
||||
_onMessageUserCallback(
|
||||
message->type,
|
||||
message->str,
|
||||
message->wireSize,
|
||||
message->errorInfo,
|
||||
message->openInfo,
|
||||
message->closeInfo
|
||||
);
|
||||
|
||||
_onMessageUserCallback(message);
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
@ -30,24 +30,12 @@ namespace ix
|
||||
void poll(int count = 512);
|
||||
|
||||
protected:
|
||||
struct Message
|
||||
{
|
||||
WebSocketMessageType type;
|
||||
std::string str;
|
||||
size_t wireSize;
|
||||
WebSocketErrorInfo errorInfo;
|
||||
WebSocketOpenInfo openInfo;
|
||||
WebSocketCloseInfo closeInfo;
|
||||
};
|
||||
|
||||
using MessagePtr = std::shared_ptr<Message>;
|
||||
|
||||
MessagePtr popMessage();
|
||||
WebSocketMessagePtr popMessage();
|
||||
|
||||
private:
|
||||
WebSocket* _websocket = nullptr;
|
||||
OnMessageCallback _onMessageUserCallback;
|
||||
std::mutex _messagesMutex;
|
||||
std::list<MessagePtr> _messages;
|
||||
std::list<WebSocketMessagePtr> _messages;
|
||||
};
|
||||
} // namespace ix
|
||||
|
21
ixwebsocket/IXWebSocketMessageType.h
Normal file
21
ixwebsocket/IXWebSocketMessageType.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* IXWebSocketMessageType.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ix
|
||||
{
|
||||
enum class WebSocketMessageType
|
||||
{
|
||||
Message = 0,
|
||||
Open = 1,
|
||||
Close = 2,
|
||||
Error = 3,
|
||||
Ping = 4,
|
||||
Pong = 5,
|
||||
Fragment = 6
|
||||
};
|
||||
}
|
24
ixwebsocket/IXWebSocketOpenInfo.h
Normal file
24
ixwebsocket/IXWebSocketOpenInfo.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* IXWebSocketOpenInfo.h
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ix
|
||||
{
|
||||
struct WebSocketOpenInfo
|
||||
{
|
||||
std::string uri;
|
||||
WebSocketHttpHeaders headers;
|
||||
|
||||
WebSocketOpenInfo(const std::string& u = std::string(),
|
||||
const WebSocketHttpHeaders& h = WebSocketHttpHeaders())
|
||||
: uri(u)
|
||||
, headers(h)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
} // namespace ix
|
@ -542,12 +542,17 @@ namespace ix
|
||||
) {
|
||||
unmaskReceiveBuffer(ws);
|
||||
|
||||
MessageKind messageKind =
|
||||
(ws.opcode == wsheader_type::TEXT_FRAME)
|
||||
? MessageKind::MSG_TEXT
|
||||
: MessageKind::MSG_BINARY;
|
||||
|
||||
//
|
||||
// Usual case. Small unfragmented messages
|
||||
//
|
||||
if (ws.fin && _chunks.empty())
|
||||
{
|
||||
emitMessage(MessageKind::MSG,
|
||||
emitMessage(messageKind,
|
||||
std::string(_rxbuf.begin()+ws.header_size,
|
||||
_rxbuf.begin()+ws.header_size+(size_t) ws.N),
|
||||
ws,
|
||||
@ -567,7 +572,7 @@ namespace ix
|
||||
_rxbuf.begin()+ws.header_size+(size_t)ws.N));
|
||||
if (ws.fin)
|
||||
{
|
||||
emitMessage(MessageKind::MSG, getMergedChunks(), ws, onMessageCallback);
|
||||
emitMessage(messageKind, getMergedChunks(), ws, onMessageCallback);
|
||||
_chunks.clear();
|
||||
}
|
||||
else
|
||||
@ -1041,7 +1046,7 @@ namespace ix
|
||||
_requestInitCancellation = true;
|
||||
|
||||
if (_readyState == ReadyState::CLOSING || _readyState == ReadyState::CLOSED) return;
|
||||
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_closeDataMutex);
|
||||
_closeCode = code;
|
||||
|
@ -50,7 +50,8 @@ namespace ix
|
||||
|
||||
enum class MessageKind
|
||||
{
|
||||
MSG,
|
||||
MSG_TEXT,
|
||||
MSG_BINARY,
|
||||
PING,
|
||||
PONG,
|
||||
FRAGMENT
|
||||
|
4
makefile
4
makefile
@ -9,7 +9,7 @@ install: brew
|
||||
# on osx it is good practice to make /usr/local user writable
|
||||
# sudo chown -R `whoami`/staff /usr/local
|
||||
brew:
|
||||
mkdir -p build && (cd build ; cmake -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j install)
|
||||
mkdir -p build && (cd build ; cmake -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j install)
|
||||
|
||||
ws:
|
||||
mkdir -p build && (cd build ; cmake -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 .. ; make -j)
|
||||
@ -44,7 +44,7 @@ trail:
|
||||
sh third_party/remote_trailing_whitespaces.sh
|
||||
|
||||
format:
|
||||
find ixwebsocket ws -name '*.cpp' -o -name '*.h' -exec clang-format -i {} \;
|
||||
find test ixwebsocket ws -name '*.cpp' -o -name '*.h' -exec clang-format -i {} \;
|
||||
|
||||
# That target is used to start a node server, but isn't required as we have
|
||||
# a builtin C++ server started in the unittest now
|
||||
|
@ -15,85 +15,219 @@ TEST_CASE("http client", "[http]")
|
||||
{
|
||||
SECTION("Connect to a remote HTTP server")
|
||||
{
|
||||
std::string url("http://httpbin.org/");
|
||||
|
||||
HttpClient httpClient;
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["User-Agent"] = "ixwebsocket";
|
||||
|
||||
HttpRequestArgs args;
|
||||
args.extraHeaders = headers;
|
||||
args.connectTimeout = 60;
|
||||
args.transferTimeout = 60;
|
||||
args.followRedirects = true;
|
||||
args.maxRedirects = 10;
|
||||
args.verbose = true;
|
||||
args.compress = true;
|
||||
args.logger = [](const std::string& msg)
|
||||
std::string url("http://httpbin.org/");
|
||||
auto args = httpClient.createRequest(url);
|
||||
|
||||
args->extraHeaders = headers;
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 60;
|
||||
args->followRedirects = true;
|
||||
args->maxRedirects = 10;
|
||||
args->verbose = true;
|
||||
args->compress = true;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
};
|
||||
args.onProgressCallback = [](int current, int total) -> bool
|
||||
args->onProgressCallback = [](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "\r" << "Downloaded "
|
||||
<< current << " bytes out of " << total;
|
||||
return true;
|
||||
};
|
||||
|
||||
HttpClient httpClient;
|
||||
HttpResponse response = httpClient.get(url, args);
|
||||
auto response = httpClient.get(url, args);
|
||||
|
||||
for (auto it : response.headers)
|
||||
for (auto it : response->headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Upload size: " << response.uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response.downloadSize << std::endl;
|
||||
std::cerr << "Status: " << response.statusCode << std::endl;
|
||||
std::cerr << "Upload size: " << response->uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response->downloadSize << std::endl;
|
||||
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||
std::cerr << "Error message: " << response->errorMsg << std::endl;
|
||||
|
||||
REQUIRE(response.errorCode == HttpErrorCode::Ok);
|
||||
REQUIRE(response->errorCode == HttpErrorCode::Ok);
|
||||
REQUIRE(response->statusCode == 200);
|
||||
}
|
||||
|
||||
#if defined(IXWEBSOCKET_USE_TLS)
|
||||
SECTION("Connect to a remote HTTPS server")
|
||||
{
|
||||
std::string url("https://httpbin.org/");
|
||||
|
||||
HttpClient httpClient;
|
||||
WebSocketHttpHeaders headers;
|
||||
headers["User-Agent"] = "ixwebsocket";
|
||||
|
||||
HttpRequestArgs args;
|
||||
args.extraHeaders = headers;
|
||||
args.connectTimeout = 60;
|
||||
args.transferTimeout = 60;
|
||||
args.followRedirects = true;
|
||||
args.maxRedirects = 10;
|
||||
args.verbose = true;
|
||||
args.compress = true;
|
||||
args.logger = [](const std::string& msg)
|
||||
std::string url("https://httpbin.org/");
|
||||
auto args = httpClient.createRequest(url);
|
||||
|
||||
args->extraHeaders = headers;
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 60;
|
||||
args->followRedirects = true;
|
||||
args->maxRedirects = 10;
|
||||
args->verbose = true;
|
||||
args->compress = true;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
};
|
||||
args.onProgressCallback = [](int current, int total) -> bool
|
||||
args->onProgressCallback = [](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "\r" << "Downloaded "
|
||||
<< current << " bytes out of " << total;
|
||||
return true;
|
||||
};
|
||||
|
||||
HttpClient httpClient;
|
||||
HttpResponse response = httpClient.get(url, args);
|
||||
auto response = httpClient.get(url, args);
|
||||
|
||||
for (auto it : response.headers)
|
||||
for (auto it : response->headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Upload size: " << response.uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response.downloadSize << std::endl;
|
||||
std::cerr << "Status: " << response.statusCode << std::endl;
|
||||
std::cerr << "Upload size: " << response->uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response->downloadSize << std::endl;
|
||||
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||
std::cerr << "Error message: " << response->errorMsg << std::endl;
|
||||
|
||||
REQUIRE(response.errorCode == HttpErrorCode::Ok);
|
||||
REQUIRE(response->errorCode == HttpErrorCode::Ok);
|
||||
REQUIRE(response->statusCode == 200);
|
||||
}
|
||||
|
||||
SECTION("Async API, one call")
|
||||
{
|
||||
bool async = true;
|
||||
HttpClient httpClient(async);
|
||||
WebSocketHttpHeaders headers;
|
||||
|
||||
std::string url("https://httpbin.org/");
|
||||
auto args = httpClient.createRequest(url);
|
||||
|
||||
args->extraHeaders = headers;
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 60;
|
||||
args->followRedirects = true;
|
||||
args->maxRedirects = 10;
|
||||
args->verbose = true;
|
||||
args->compress = true;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
};
|
||||
args->onProgressCallback = [](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "\r" << "Downloaded "
|
||||
<< current << " bytes out of " << total;
|
||||
return true;
|
||||
};
|
||||
|
||||
std::atomic<bool> requestCompleted(false);
|
||||
std::atomic<int> statusCode(0);
|
||||
|
||||
httpClient.performRequest(args, [&requestCompleted, &statusCode]
|
||||
(const HttpResponsePtr& response)
|
||||
{
|
||||
std::cerr << "Upload size: " << response->uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response->downloadSize << std::endl;
|
||||
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||
std::cerr << "Error message: " << response->errorMsg << std::endl;
|
||||
|
||||
// In case of failure, print response->errorMsg
|
||||
statusCode = response->statusCode;
|
||||
requestCompleted = true;
|
||||
}
|
||||
);
|
||||
|
||||
int wait = 0;
|
||||
while (wait < 5000)
|
||||
{
|
||||
if (requestCompleted) break;
|
||||
|
||||
std::chrono::duration<double, std::milli> duration(10);
|
||||
std::this_thread::sleep_for(duration);
|
||||
wait += 10;
|
||||
}
|
||||
|
||||
std::cerr << "Done" << std::endl;
|
||||
REQUIRE(statusCode == 200);
|
||||
}
|
||||
|
||||
SECTION("Async API, multiple calls")
|
||||
{
|
||||
bool async = true;
|
||||
HttpClient httpClient(async);
|
||||
WebSocketHttpHeaders headers;
|
||||
|
||||
std::string url("http://httpbin.org/");
|
||||
auto args = httpClient.createRequest(url);
|
||||
|
||||
args->extraHeaders = headers;
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 60;
|
||||
args->followRedirects = true;
|
||||
args->maxRedirects = 10;
|
||||
args->verbose = true;
|
||||
args->compress = true;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
};
|
||||
args->onProgressCallback = [](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "\r" << "Downloaded "
|
||||
<< current << " bytes out of " << total;
|
||||
return true;
|
||||
};
|
||||
|
||||
std::atomic<bool> requestCompleted(false);
|
||||
std::atomic<int> statusCode0(0);
|
||||
std::atomic<int> statusCode1(0);
|
||||
std::atomic<int> statusCode2(0);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
httpClient.performRequest(args, [i, &requestCompleted, &statusCode0, &statusCode1, &statusCode2]
|
||||
(const HttpResponsePtr& response)
|
||||
{
|
||||
std::cerr << "Upload size: " << response->uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response->downloadSize << std::endl;
|
||||
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||
std::cerr << "Error message: " << response->errorMsg << std::endl;
|
||||
|
||||
// In case of failure, print response->errorMsg
|
||||
if (i == 0)
|
||||
{
|
||||
statusCode0 = response->statusCode;
|
||||
}
|
||||
else if (i == 1)
|
||||
{
|
||||
statusCode1 = response->statusCode;
|
||||
}
|
||||
else if (i == 2)
|
||||
{
|
||||
statusCode2 = response->statusCode;
|
||||
requestCompleted = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
int wait = 0;
|
||||
while (wait < 10000)
|
||||
{
|
||||
if (requestCompleted) break;
|
||||
|
||||
std::chrono::duration<double, std::milli> duration(10);
|
||||
std::this_thread::sleep_for(duration);
|
||||
wait += 10;
|
||||
}
|
||||
|
||||
std::cerr << "Done" << std::endl;
|
||||
REQUIRE(statusCode0 == 200);
|
||||
REQUIRE(statusCode1 == 200);
|
||||
REQUIRE(statusCode2 == 200);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -178,34 +178,29 @@ namespace ix
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
Logger() << "Closed connection";
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(str);
|
||||
client->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <mutex>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ix
|
||||
{
|
||||
@ -28,20 +28,20 @@ namespace ix
|
||||
|
||||
struct Logger
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
Logger& operator<<(T const& obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
public:
|
||||
template<typename T>
|
||||
Logger& operator<<(T const& obj)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << obj;
|
||||
spdlog::info(ss.str());
|
||||
return *this;
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << obj;
|
||||
spdlog::info(ss.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::mutex _mutex;
|
||||
private:
|
||||
static std::mutex _mutex;
|
||||
};
|
||||
|
||||
void log(const std::string& msg);
|
||||
@ -49,4 +49,4 @@ namespace ix
|
||||
int getFreePort();
|
||||
|
||||
bool startWebSocketEchoServer(ix::WebSocketServer& server);
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -108,52 +108,47 @@ namespace
|
||||
log(std::string("Connecting to url: ") + url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log("client connected");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "client disconnected("
|
||||
<< closeInfo.code
|
||||
<< msg->closeInfo.code
|
||||
<< ","
|
||||
<< closeInfo.reason
|
||||
<< msg->closeInfo.reason
|
||||
<< ")";
|
||||
log(ss.str());
|
||||
|
||||
std::lock_guard<std::mutex> lck(_mutexCloseData);
|
||||
|
||||
_closeCode = closeInfo.code;
|
||||
_closeReason = std::string(closeInfo.reason);
|
||||
_closeRemote = closeInfo.remote;
|
||||
_closeCode = msg->closeInfo.code;
|
||||
_closeReason = std::string(msg->closeInfo.reason);
|
||||
_closeRemote = msg->closeInfo.remote;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "Error ! " << error.reason;
|
||||
ss << "Error ! " << msg->errorInfo.reason;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
ss << "Received pong message " << str;
|
||||
ss << "Received pong message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
ss << "Received ping message " << str;
|
||||
ss << "Received ping message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
ss << "Received message " << str;
|
||||
ss << "Received message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else
|
||||
@ -183,39 +178,34 @@ namespace
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, &server, &receivedCloseCode, &receivedCloseReason, &receivedCloseRemote, &mutexWrite](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New server connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Server closed connection("
|
||||
<< closeInfo.code
|
||||
<< msg->closeInfo.code
|
||||
<< ","
|
||||
<< closeInfo.reason
|
||||
<< msg->closeInfo.reason
|
||||
<< ")";
|
||||
log(ss.str());
|
||||
|
||||
|
||||
std::lock_guard<std::mutex> lck(mutexWrite);
|
||||
|
||||
receivedCloseCode = closeInfo.code;
|
||||
receivedCloseReason = std::string(closeInfo.reason);
|
||||
receivedCloseRemote = closeInfo.remote;
|
||||
receivedCloseCode = msg->closeInfo.code;
|
||||
receivedCloseReason = std::string(msg->closeInfo.reason);
|
||||
receivedCloseRemote = msg->closeInfo.remote;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -20,39 +20,34 @@ namespace
|
||||
{
|
||||
server.setOnConnectionCallback(
|
||||
[&server](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[connectionState, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string & str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo & error,
|
||||
const ix::WebSocketOpenInfo & openInfo,
|
||||
const ix::WebSocketCloseInfo & closeInfo)
|
||||
[connectionState, &server](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
connectionState->computeId();
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto&& it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
Logger() << "Closed connection";
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
Logger() << "Message received: " << str;
|
||||
Logger() << "Message received: " << msg->str;
|
||||
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
client->send(str);
|
||||
client->send(msg->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,46 +73,41 @@ namespace
|
||||
{
|
||||
msgQ.bindWebsocket(&ws);
|
||||
|
||||
msgQ.setOnMessageCallback([this](WebSocketMessageType messageType,
|
||||
const std::string & str,
|
||||
size_t wireSize,
|
||||
const WebSocketErrorInfo & error,
|
||||
const WebSocketOpenInfo & openInfo,
|
||||
const WebSocketCloseInfo & closeInfo)
|
||||
msgQ.setOnMessageCallback([this](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
REQUIRE(mainThreadId == std::this_thread::get_id());
|
||||
|
||||
std::stringstream ss;
|
||||
if (messageType == WebSocketMessageType::Open)
|
||||
if (msg->type == WebSocketMessageType::Open)
|
||||
{
|
||||
log("client connected");
|
||||
sendNextMessage();
|
||||
}
|
||||
else if (messageType == WebSocketMessageType::Close)
|
||||
else if (msg->type == WebSocketMessageType::Close)
|
||||
{
|
||||
log("client disconnected");
|
||||
}
|
||||
else if (messageType == WebSocketMessageType::Error)
|
||||
else if (msg->type == WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "Error ! " << error.reason;
|
||||
ss << "Error ! " << msg->errorInfo.reason;
|
||||
log(ss.str());
|
||||
testDone = true;
|
||||
}
|
||||
else if (messageType == WebSocketMessageType::Pong)
|
||||
else if (msg->type == WebSocketMessageType::Pong)
|
||||
{
|
||||
ss << "Received pong message " << str;
|
||||
ss << "Received pong message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == WebSocketMessageType::Ping)
|
||||
else if (msg->type == WebSocketMessageType::Ping)
|
||||
{
|
||||
ss << "Received ping message " << str;
|
||||
ss << "Received ping message " << msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == WebSocketMessageType::Message)
|
||||
else if (msg->type == WebSocketMessageType::Message)
|
||||
{
|
||||
REQUIRE(str.compare("Hey dude!") == 0);
|
||||
REQUIRE(msg->str.compare("Hey dude!") == 0);
|
||||
++receivedCount;
|
||||
ss << "Received message " << str;
|
||||
ss << "Received message " << msg->str;
|
||||
log(ss.str());
|
||||
sendNextMessage();
|
||||
}
|
||||
@ -189,5 +179,4 @@ TEST_CASE("Websocket_message_queue", "[websocket_message_q]")
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ namespace
|
||||
{
|
||||
log("client disconnected");
|
||||
|
||||
if (closeInfo.code == 1011)
|
||||
if (msg->closeInfo.code == 1011)
|
||||
{
|
||||
_closedDueToPingTimeout = true;
|
||||
}
|
||||
|
@ -39,42 +39,37 @@ namespace ix
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
[&server, &connectionId](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState,
|
||||
&connectionId, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
&connectionId, &server](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
connectionState->computeId();
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
}
|
||||
|
||||
connectionId = connectionState->getId();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
Logger() << "Closed connection";
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(str);
|
||||
client->send(msg->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,41 +52,36 @@ namespace
|
||||
log(std::string("Connecting to url: ") + url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log("TestConnectionDisconnection: connected !");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log("TestConnectionDisconnection: disconnected !");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "TestConnectionDisconnection: Error! ";
|
||||
ss << error.reason;
|
||||
ss << msg->errorInfo.reason;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
log("TestConnectionDisconnection: received message.!");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
log("TestConnectionDisconnection: received ping message.!");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
log("TestConnectionDisconnection: received pong message.!");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
log("TestConnectionDisconnection: received fragment.!");
|
||||
}
|
||||
|
@ -114,31 +114,26 @@ namespace
|
||||
log(std::string("Connecting to url: ") + url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
ss << "cmd_websocket_chat: user "
|
||||
<< _user
|
||||
<< " Connected !";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "cmd_websocket_chat: user "
|
||||
<< _user
|
||||
<< " disconnected !";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
auto result = decodeMessage(str);
|
||||
auto result = decodeMessage(msg->str);
|
||||
|
||||
// Our "chat" / "broacast" node.js server does not send us
|
||||
// the messages we send, so we don't need to have a msg_user != user
|
||||
@ -159,20 +154,20 @@ namespace
|
||||
<< _user << " > ";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "cmd_websocket_chat: Error ! " << error.reason;
|
||||
ss << "cmd_websocket_chat: Error ! " << msg->errorInfo.reason;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
log("cmd_websocket_chat: received ping message");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
log("cmd_websocket_chat: received pong message");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
log("cmd_websocket_chat: received message fragment");
|
||||
}
|
||||
@ -221,35 +216,30 @@ namespace
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, &server](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
Logger() << "New connection";
|
||||
Logger() << "id: " << connectionState->getId();
|
||||
Logger() << "Uri: " << openInfo.uri;
|
||||
Logger() << "Uri: " << msg->openInfo.uri;
|
||||
Logger() << "Headers:";
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
Logger() << it.first << ": " << it.second;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
log("Closed connection");
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(str);
|
||||
client->send(msg->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
test/compatibility/python/websockets/README.md
Normal file
30
test/compatibility/python/websockets/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Clients
|
||||
|
||||
## ws
|
||||
|
||||
```
|
||||
$ ws connect ws://127.0.0.1:8765
|
||||
Type Ctrl-D to exit prompt...
|
||||
Connecting to url: ws://127.0.0.1:8765
|
||||
> ws_connect: connected
|
||||
Uri: /
|
||||
Handshake Headers:
|
||||
Connection: Upgrade
|
||||
Date: Sat, 08 Jun 2019 16:43:29 GMT
|
||||
Sec-WebSocket-Accept: kPCNwGa97y+7NWdAvHi/7/rA8AE=
|
||||
Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
|
||||
Server: Python/3.7 websockets/7.0
|
||||
Upgrade: websocket
|
||||
Received 13 bytes
|
||||
ws_connect: received message: > Welcome !
|
||||
ws_connect: connection closed: code 1006 reason Abnormal closure
|
||||
```
|
||||
|
||||
## wscat
|
||||
|
||||
```
|
||||
$ ./node_modules/.bin/wscat -c ws://127.0.0.1:8765
|
||||
connected (press CTRL+C to quit)
|
||||
< > Welcome !
|
||||
disconnected (code: 1006)
|
||||
```
|
22
test/compatibility/python/websockets/echo_server.py
Normal file
22
test/compatibility/python/websockets/echo_server.py
Normal file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# WS server example
|
||||
|
||||
import asyncio
|
||||
import websockets
|
||||
|
||||
async def hello(websocket, path):
|
||||
await websocket.send(f"> Welcome !")
|
||||
|
||||
name = await websocket.recv()
|
||||
print(f"< {name}")
|
||||
|
||||
greeting = f"Hello {name}!"
|
||||
|
||||
await websocket.send(greeting)
|
||||
print(f"> {greeting}")
|
||||
|
||||
start_server = websockets.serve(hello, 'localhost', 8765)
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(start_server)
|
||||
asyncio.get_event_loop().run_forever()
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <ixwebsocket/IXWebSocketHttpHeaders.h>
|
||||
|
||||
@ -114,6 +115,7 @@ namespace ix
|
||||
std::string SentryClient::computePayload(const Json::Value& msg)
|
||||
{
|
||||
Json::Value payload;
|
||||
|
||||
payload["platform"] = "python";
|
||||
payload["sdk"]["name"] = "ws";
|
||||
payload["sdk"]["version"] = "1.0.0";
|
||||
@ -132,51 +134,77 @@ namespace ix
|
||||
|
||||
Json::Value extra;
|
||||
extra["cobra_event"] = msg;
|
||||
extra["cobra_event"] = msg;
|
||||
|
||||
exception["extra"] = extra;
|
||||
//
|
||||
// "tags": [
|
||||
// [
|
||||
// "a",
|
||||
// "b"
|
||||
// ],
|
||||
// ]
|
||||
//
|
||||
Json::Value tags;
|
||||
|
||||
Json::Value gameTag;
|
||||
gameTag.append("game");
|
||||
gameTag.append(msg["device"]["game"]);
|
||||
tags.append(gameTag);
|
||||
|
||||
Json::Value userIdTag;
|
||||
userIdTag.append("userid");
|
||||
userIdTag.append(msg["device"]["user_id"]);
|
||||
tags.append(userIdTag);
|
||||
|
||||
Json::Value environmentTag;
|
||||
environmentTag.append("environment");
|
||||
environmentTag.append(msg["device"]["environment"]);
|
||||
tags.append(environmentTag);
|
||||
|
||||
payload["tags"] = tags;
|
||||
|
||||
return _jsonWriter.write(payload);
|
||||
}
|
||||
|
||||
bool SentryClient::send(const Json::Value& msg,
|
||||
bool verbose)
|
||||
std::pair<HttpResponsePtr, std::string> SentryClient::send(const Json::Value& msg,
|
||||
bool verbose)
|
||||
{
|
||||
HttpRequestArgs args;
|
||||
args.extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
|
||||
args.connectTimeout = 60;
|
||||
args.transferTimeout = 5 * 60;
|
||||
args.followRedirects = true;
|
||||
args.verbose = verbose;
|
||||
args.logger = [](const std::string& msg)
|
||||
auto args = _httpClient.createRequest();
|
||||
args->extraHeaders["X-Sentry-Auth"] = SentryClient::computeAuthHeader();
|
||||
args->connectTimeout = 60;
|
||||
args->transferTimeout = 5 * 60;
|
||||
args->followRedirects = true;
|
||||
args->verbose = verbose;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
spdlog::info("request logger: {}", msg);
|
||||
};
|
||||
|
||||
std::string body = computePayload(msg);
|
||||
HttpResponse response = _httpClient.post(_url, body, args);
|
||||
HttpResponsePtr response = _httpClient.post(_url, body, args);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
for (auto it : response.headers)
|
||||
for (auto it : response->headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
|
||||
std::cerr << "Upload size: " << response.uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response.downloadSize << std::endl;
|
||||
spdlog::info("Upload size: {}", response->uploadSize);
|
||||
spdlog::info("Download size: {}", response->downloadSize);
|
||||
|
||||
std::cerr << "Status: " << response.statusCode << std::endl;
|
||||
if (response.errorCode != HttpErrorCode::Ok)
|
||||
spdlog::info("Status: {}", response->statusCode);
|
||||
if (response->errorCode != HttpErrorCode::Ok)
|
||||
{
|
||||
std::cerr << "error message: " << response.errorMsg << std::endl;
|
||||
spdlog::info("error message: {}", response->errorMsg);
|
||||
}
|
||||
|
||||
if (response.headers["Content-Type"] != "application/octet-stream")
|
||||
if (response->headers["Content-Type"] != "application/octet-stream")
|
||||
{
|
||||
std::cerr << "payload: " << response.payload << std::endl;
|
||||
spdlog::info("payload: {}", response->payload);
|
||||
}
|
||||
}
|
||||
|
||||
return response.statusCode == 200;
|
||||
return std::make_pair(response, body);
|
||||
}
|
||||
} // namespace ix
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <ixwebsocket/IXHttpClient.h>
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <regex>
|
||||
@ -18,7 +19,7 @@ namespace ix
|
||||
SentryClient(const std::string& dsn);
|
||||
~SentryClient() = default;
|
||||
|
||||
bool send(const Json::Value& msg, bool verbose);
|
||||
std::pair<HttpResponsePtr, std::string> send(const Json::Value& msg, bool verbose);
|
||||
|
||||
private:
|
||||
int64_t getTimestamp();
|
||||
|
@ -90,46 +90,41 @@ namespace ix
|
||||
void CobraConnection::initWebSocketOnMessageCallback()
|
||||
{
|
||||
_webSocket->setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
CobraConnection::invokeTrafficTrackerCallback(wireSize, true);
|
||||
CobraConnection::invokeTrafficTrackerCallback(msg->wireSize, true);
|
||||
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Open,
|
||||
std::string(),
|
||||
openInfo.headers);
|
||||
msg->openInfo.headers);
|
||||
sendHandshakeMessage();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
_authenticated = false;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Close code " << closeInfo.code;
|
||||
ss << " reason " << closeInfo.reason;
|
||||
ss << "Close code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason;
|
||||
invokeEventCallback(ix::CobraConnection_EventType_Closed,
|
||||
ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
Json::Value data;
|
||||
Json::Reader reader;
|
||||
if (!reader.parse(str, data))
|
||||
if (!reader.parse(msg->str, data))
|
||||
{
|
||||
invokeErrorCallback("Invalid json", str);
|
||||
invokeErrorCallback("Invalid json", msg->str);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.isMember("action"))
|
||||
{
|
||||
invokeErrorCallback("Missing action", str);
|
||||
invokeErrorCallback("Missing action", msg->str);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -139,12 +134,12 @@ namespace ix
|
||||
{
|
||||
if (!handleHandshakeResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error extracting nonce from handshake response", str);
|
||||
invokeErrorCallback("Error extracting nonce from handshake response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "auth/handshake/error")
|
||||
{
|
||||
invokeErrorCallback("Handshake error", str);
|
||||
invokeErrorCallback("Handshake error", msg->str);
|
||||
}
|
||||
else if (action == "auth/authenticate/ok")
|
||||
{
|
||||
@ -154,7 +149,7 @@ namespace ix
|
||||
}
|
||||
else if (action == "auth/authenticate/error")
|
||||
{
|
||||
invokeErrorCallback("Authentication error", str);
|
||||
invokeErrorCallback("Authentication error", msg->str);
|
||||
}
|
||||
else if (action == "rtm/subscription/data")
|
||||
{
|
||||
@ -164,36 +159,36 @@ namespace ix
|
||||
{
|
||||
if (!handleSubscriptionResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing subscribe response", str);
|
||||
invokeErrorCallback("Error processing subscribe response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/subscribe/error")
|
||||
{
|
||||
invokeErrorCallback("Subscription error", str);
|
||||
invokeErrorCallback("Subscription error", msg->str);
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/ok")
|
||||
{
|
||||
if (!handleUnsubscriptionResponse(data))
|
||||
{
|
||||
invokeErrorCallback("Error processing subscribe response", str);
|
||||
invokeErrorCallback("Error processing subscribe response", msg->str);
|
||||
}
|
||||
}
|
||||
else if (action == "rtm/unsubscribe/error")
|
||||
{
|
||||
invokeErrorCallback("Unsubscription error", str);
|
||||
invokeErrorCallback("Unsubscription error", msg->str);
|
||||
}
|
||||
else
|
||||
{
|
||||
invokeErrorCallback("Un-handled message type", str);
|
||||
invokeErrorCallback("Un-handled message type", msg->str);
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
invokeErrorCallback(ss.str(), std::string());
|
||||
}
|
||||
});
|
||||
|
@ -99,7 +99,7 @@ namespace ix
|
||||
void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind,
|
||||
const Json::Value& msg)
|
||||
{
|
||||
// Now actually enqueue the task
|
||||
// Enqueue the task
|
||||
{
|
||||
// acquire lock
|
||||
std::unique_lock<std::mutex> lock(_queue_mutex);
|
||||
|
@ -25,7 +25,6 @@ namespace ix
|
||||
~CobraMetricsThreadedPublisher();
|
||||
|
||||
/// Configuration / set keys, etc...
|
||||
/// All input data but the channel name is encrypted with rc4
|
||||
void configure(const std::string& appkey,
|
||||
const std::string& endpoint,
|
||||
const std::string& channel,
|
||||
|
@ -10,7 +10,6 @@
|
||||
#if defined(IXWEBSOCKET_USE_MBED_TLS)
|
||||
# include <mbedtls/md.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <ixwebsocket/IXSocketMbedTLS.h>
|
||||
# include <CommonCrypto/CommonHMAC.h>
|
||||
#else
|
||||
# include <openssl/hmac.h>
|
||||
|
@ -58,25 +58,20 @@ namespace snake
|
||||
auto state = std::dynamic_pointer_cast<SnakeConnectionState>(connectionState);
|
||||
|
||||
webSocket->setOnMessageCallback(
|
||||
[this, webSocket, state](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this, webSocket, state](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cerr << "New connection" << std::endl;
|
||||
std::cerr << "id: " << state->getId() << std::endl;
|
||||
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cerr << "Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
|
||||
std::string appkey = parseAppKey(openInfo.uri);
|
||||
std::string appkey = parseAppKey(msg->openInfo.uri);
|
||||
state->setAppkey(appkey);
|
||||
|
||||
// Connect to redis first
|
||||
@ -86,29 +81,29 @@ namespace snake
|
||||
std::cerr << "Cannot connect to redis host" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::cerr << "Closed connection"
|
||||
<< " code " << closeInfo.code
|
||||
<< " reason " << closeInfo.reason << std::endl;
|
||||
<< " code " << msg->closeInfo.code
|
||||
<< " reason " << msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
std::cerr << ss.str();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
std::cerr << "Received message fragment" << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
||||
processCobraMessage(state, webSocket, _appConfig, str);
|
||||
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
|
||||
processCobraMessage(state, webSocket, _appConfig, msg->str);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -80,6 +80,8 @@ int main(int argc, char** argv)
|
||||
bool strict = false;
|
||||
bool stress = false;
|
||||
bool disableAutomaticReconnection = false;
|
||||
bool disablePerMessageDeflate = false;
|
||||
bool greetings = false;
|
||||
int port = 8008;
|
||||
int redisPort = 6379;
|
||||
int statsdPort = 8125;
|
||||
@ -110,6 +112,7 @@ int main(int argc, char** argv)
|
||||
CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server");
|
||||
connectApp->add_option("url", url, "Connection url")->required();
|
||||
connectApp->add_flag("-d", disableAutomaticReconnection, "Disable Automatic Reconnection");
|
||||
connectApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
|
||||
|
||||
CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
|
||||
chatApp->add_option("url", url, "Connection url")->required();
|
||||
@ -118,6 +121,7 @@ int main(int argc, char** argv)
|
||||
CLI::App* echoServerApp = app.add_subcommand("echo_server", "Echo server");
|
||||
echoServerApp->add_option("--port", port, "Port");
|
||||
echoServerApp->add_option("--host", hostname, "Hostname");
|
||||
echoServerApp->add_flag("-g", greetings, "Verbose");
|
||||
|
||||
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
|
||||
broadcastServerApp->add_option("--port", port, "Port");
|
||||
@ -241,7 +245,8 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (app.got_subcommand("connect"))
|
||||
{
|
||||
ret = ix::ws_connect_main(url, disableAutomaticReconnection);
|
||||
ret = ix::ws_connect_main(url, disableAutomaticReconnection,
|
||||
disablePerMessageDeflate);
|
||||
}
|
||||
else if (app.got_subcommand("chat"))
|
||||
{
|
||||
@ -249,7 +254,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
else if (app.got_subcommand("echo_server"))
|
||||
{
|
||||
ret = ix::ws_echo_server_main(port, hostname);
|
||||
ret = ix::ws_echo_server_main(port, greetings, hostname);
|
||||
}
|
||||
else if (app.got_subcommand("broadcast_server"))
|
||||
{
|
||||
|
6
ws/ws.h
6
ws/ws.h
@ -24,13 +24,15 @@ namespace ix
|
||||
|
||||
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, bool greetings, const std::string& hostname);
|
||||
int ws_broadcast_server_main(int port, const std::string& hostname);
|
||||
int ws_transfer_main(int port, const std::string& hostname);
|
||||
|
||||
int ws_chat_main(const std::string& url, const std::string& user);
|
||||
|
||||
int ws_connect_main(const std::string& url, bool disableAutomaticReconnection);
|
||||
int ws_connect_main(const std::string& url,
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate);
|
||||
|
||||
int ws_receive_main(const std::string& url, bool enablePerMessageDeflate, int delayMs);
|
||||
|
||||
|
@ -21,52 +21,48 @@ namespace ix
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, &server](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cerr << "New connection" << std::endl;
|
||||
std::cerr << "id: " << connectionState->getId() << std::endl;
|
||||
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cerr << "Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::cerr << "Closed connection"
|
||||
<< " code " << closeInfo.code
|
||||
<< " reason " << closeInfo.reason << std::endl;
|
||||
<< " code " << msg->closeInfo.code
|
||||
<< " reason " << msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
std::cerr << ss.str();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
std::cerr << "Received message fragment" << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
||||
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
|
||||
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(str,
|
||||
client->send(msg->str,
|
||||
msg->binary,
|
||||
[](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "Step " << current
|
||||
|
@ -84,20 +84,15 @@ namespace ix
|
||||
log(std::string("Connecting to url: ") + _url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log("ws chat: connected");
|
||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cout << "Handshake Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
@ -107,18 +102,18 @@ namespace ix
|
||||
<< " Connected !";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "ws chat: user "
|
||||
<< _user
|
||||
<< " disconnected !"
|
||||
<< " code " << closeInfo.code
|
||||
<< " reason " << closeInfo.reason;
|
||||
<< " code " << msg->closeInfo.code
|
||||
<< " reason " << msg->closeInfo.reason;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
auto result = decodeMessage(str);
|
||||
auto result = decodeMessage(msg->str);
|
||||
|
||||
// Our "chat" / "broacast" node.js server does not send us
|
||||
// the messages we send, so we don't have to filter it out.
|
||||
@ -127,17 +122,17 @@ namespace ix
|
||||
_receivedQueue.push(result.second);
|
||||
|
||||
ss << std::endl
|
||||
<< result.first << "(" << wireSize << " bytes)" << " > " << result.second
|
||||
<< result.first << "(" << msg->wireSize << " bytes)" << " > " << result.second
|
||||
<< std::endl
|
||||
<< _user << " > ";
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else
|
||||
@ -172,7 +167,7 @@ namespace ix
|
||||
|
||||
void WebSocketChat::sendMessage(const std::string& text)
|
||||
{
|
||||
_webSocket.send(encodeMessage(text));
|
||||
_webSocket.sendText(encodeMessage(text));
|
||||
}
|
||||
|
||||
int ws_chat_main(const std::string& url,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <ixcobra/IXCobraConnection.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "IXSentryClient.h"
|
||||
|
||||
@ -50,6 +51,8 @@ namespace ix
|
||||
&queue, verbose, &errorSending, &sentCount,
|
||||
&stop, &dsn]
|
||||
{
|
||||
SentryClient sentryClient(dsn);
|
||||
|
||||
while (true)
|
||||
{
|
||||
Json::Value msg;
|
||||
@ -62,10 +65,13 @@ namespace ix
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
SentryClient sc(dsn);
|
||||
|
||||
if (!sc.send(msg, verbose))
|
||||
auto ret = sentryClient.send(msg, verbose);
|
||||
HttpResponsePtr response = ret.first;
|
||||
if (response->statusCode != 200)
|
||||
{
|
||||
spdlog::error("Error sending data to sentry: {}", response->statusCode);
|
||||
spdlog::error("Body: {}", ret.second);
|
||||
spdlog::error("Response: {}", response->payload);
|
||||
errorSending = true;
|
||||
}
|
||||
else
|
||||
@ -99,16 +105,16 @@ namespace ix
|
||||
{
|
||||
if (eventType == ix::CobraConnection_EventType_Open)
|
||||
{
|
||||
std::cerr << "Subscriber: connected" << std::endl;
|
||||
spdlog::info("Subscriber connected");
|
||||
|
||||
for (auto it : headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
spdlog::info("{}: {}", it.first, it.second);
|
||||
}
|
||||
}
|
||||
if (eventType == ix::CobraConnection_EventType_Closed)
|
||||
{
|
||||
std::cerr << "Subscriber: closed" << std::endl;
|
||||
spdlog::info("Subscriber closed");
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Authenticated)
|
||||
{
|
||||
@ -122,7 +128,7 @@ namespace ix
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << jsonWriter.write(msg) << std::endl;
|
||||
spdlog::info(jsonWriter.write(msg));
|
||||
}
|
||||
|
||||
// If we cannot send to sentry fast enough, drop the message
|
||||
@ -132,8 +138,7 @@ namespace ix
|
||||
receivedCount != 0 &&
|
||||
(sentCount * scaleFactor < receivedCount))
|
||||
{
|
||||
std::cerr << "message dropped: sending is backlogged !"
|
||||
<< std::endl;
|
||||
spdlog::warn("message dropped: sending is backlogged !");
|
||||
|
||||
condition.notify_one();
|
||||
progressCondition.notify_one();
|
||||
@ -153,15 +158,15 @@ namespace ix
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Subscribed)
|
||||
{
|
||||
std::cerr << "Subscriber: subscribed to channel " << subscriptionId << std::endl;
|
||||
spdlog::info("Subscriber: subscribed to channel {}", subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_UnSubscribed)
|
||||
{
|
||||
std::cerr << "Subscriber: unsubscribed from channel " << subscriptionId << std::endl;
|
||||
spdlog::info("Subscriber: unsubscribed from channel {}", subscriptionId);
|
||||
}
|
||||
else if (eventType == ix::CobraConnection_EventType_Error)
|
||||
{
|
||||
std::cerr << "Subscriber: error" << errMsg << std::endl;
|
||||
spdlog::error("Subscriber: error {}", errMsg);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -172,18 +177,21 @@ namespace ix
|
||||
std::unique_lock<std::mutex> lock(progressConditionVariableMutex);
|
||||
progressCondition.wait(lock);
|
||||
|
||||
std::cout << "messages"
|
||||
<< " received " << receivedCount
|
||||
<< " sent " << sentCount
|
||||
<< std::endl;
|
||||
spdlog::info("messages received {} sent {}", receivedCount, sentCount);
|
||||
|
||||
if (strict && errorSending) break;
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
|
||||
// FIXME: join all the bg threads and stop them.
|
||||
// join all the bg threads and stop them.
|
||||
stop = true;
|
||||
for (int i = 0; i < jobs; i++)
|
||||
{
|
||||
spdlog::error("joining thread {}", i);
|
||||
pool[i].join();
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (strict && errorSending) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ namespace ix
|
||||
{
|
||||
public:
|
||||
WebSocketConnect(const std::string& _url,
|
||||
bool disableAutomaticReconnection);
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate);
|
||||
|
||||
void subscribe(const std::string& channel);
|
||||
void start();
|
||||
@ -26,13 +27,16 @@ namespace ix
|
||||
private:
|
||||
std::string _url;
|
||||
ix::WebSocket _webSocket;
|
||||
bool _disablePerMessageDeflate;
|
||||
|
||||
void log(const std::string& msg);
|
||||
};
|
||||
|
||||
WebSocketConnect::WebSocketConnect(const std::string& url,
|
||||
bool disableAutomaticReconnection) :
|
||||
_url(url)
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate) :
|
||||
_url(url),
|
||||
_disablePerMessageDeflate(disablePerMessageDeflate)
|
||||
{
|
||||
if (disableAutomaticReconnection)
|
||||
{
|
||||
@ -54,64 +58,66 @@ namespace ix
|
||||
{
|
||||
_webSocket.setUrl(_url);
|
||||
|
||||
ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
|
||||
true, false, false, 15, 15);
|
||||
_webSocket.setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
||||
if (_disablePerMessageDeflate)
|
||||
{
|
||||
_webSocket.disablePerMessageDeflate();
|
||||
}
|
||||
else
|
||||
{
|
||||
ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(
|
||||
true, false, false, 15, 15);
|
||||
_webSocket.setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
log(std::string("Connecting to url: ") + _url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log("ws_connect: connected");
|
||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cout << "Handshake Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "ws_connect: connection closed:";
|
||||
ss << " code " << closeInfo.code;
|
||||
ss << " reason " << closeInfo.reason << std::endl;
|
||||
ss << " code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
||||
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
|
||||
|
||||
ss << "ws_connect: received message: "
|
||||
<< str;
|
||||
<< msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
std::cerr << "Received message fragment" << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
std::cerr << "Received ping" << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
std::cerr << "Received pong" << std::endl;
|
||||
}
|
||||
@ -127,13 +133,17 @@ namespace ix
|
||||
|
||||
void WebSocketConnect::sendMessage(const std::string& text)
|
||||
{
|
||||
_webSocket.send(text);
|
||||
_webSocket.sendText(text);
|
||||
}
|
||||
|
||||
int ws_connect_main(const std::string& url, bool disableAutomaticReconnection)
|
||||
int ws_connect_main(const std::string& url,
|
||||
bool disableAutomaticReconnection,
|
||||
bool disablePerMessageDeflate)
|
||||
{
|
||||
std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
|
||||
WebSocketConnect webSocketChat(url, disableAutomaticReconnection);
|
||||
WebSocketConnect webSocketChat(url,
|
||||
disableAutomaticReconnection,
|
||||
disablePerMessageDeflate);
|
||||
webSocketChat.start();
|
||||
|
||||
while (true)
|
||||
|
@ -10,56 +10,56 @@
|
||||
|
||||
namespace ix
|
||||
{
|
||||
int ws_echo_server_main(int port, const std::string& hostname)
|
||||
int ws_echo_server_main(int port, bool greetings, const std::string& hostname)
|
||||
{
|
||||
std::cout << "Listening on " << hostname << ":" << port << std::endl;
|
||||
|
||||
ix::WebSocketServer server(port, hostname);
|
||||
|
||||
server.setOnConnectionCallback(
|
||||
[](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
[greetings](std::shared_ptr<ix::WebSocket> webSocket,
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, greetings](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cerr << "New connection" << std::endl;
|
||||
std::cerr << "id: " << connectionState->getId() << std::endl;
|
||||
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cerr << "Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
|
||||
if (greetings)
|
||||
{
|
||||
webSocket->sendText("Welcome !");
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::cerr << "Closed connection"
|
||||
<< " code " << closeInfo.code
|
||||
<< " reason " << closeInfo.reason << std::endl;
|
||||
<< " code " << msg->closeInfo.code
|
||||
<< " reason " << msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
std::cerr << ss.str();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
std::cerr << "Received "
|
||||
<< wireSize << " bytes"
|
||||
<< msg->wireSize << " bytes"
|
||||
<< std::endl;
|
||||
webSocket->send(str);
|
||||
webSocket->send(msg->str, msg->binary);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -95,19 +95,20 @@ namespace ix
|
||||
const std::string& output,
|
||||
bool compress)
|
||||
{
|
||||
HttpRequestArgs args;
|
||||
args.extraHeaders = parseHeaders(headersData);
|
||||
args.connectTimeout = connectTimeout;
|
||||
args.transferTimeout = transferTimeout;
|
||||
args.followRedirects = followRedirects;
|
||||
args.maxRedirects = maxRedirects;
|
||||
args.verbose = verbose;
|
||||
args.compress = compress;
|
||||
args.logger = [](const std::string& msg)
|
||||
HttpClient httpClient;
|
||||
auto args = httpClient.createRequest();
|
||||
args->extraHeaders = parseHeaders(headersData);
|
||||
args->connectTimeout = connectTimeout;
|
||||
args->transferTimeout = transferTimeout;
|
||||
args->followRedirects = followRedirects;
|
||||
args->maxRedirects = maxRedirects;
|
||||
args->verbose = verbose;
|
||||
args->compress = compress;
|
||||
args->logger = [](const std::string& msg)
|
||||
{
|
||||
std::cout << msg;
|
||||
};
|
||||
args.onProgressCallback = [](int current, int total) -> bool
|
||||
args->onProgressCallback = [](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "\r" << "Downloaded "
|
||||
<< current << " bytes out of " << total;
|
||||
@ -116,8 +117,7 @@ namespace ix
|
||||
|
||||
HttpParameters httpParameters = parsePostParameters(data);
|
||||
|
||||
HttpClient httpClient;
|
||||
HttpResponse response;
|
||||
HttpResponsePtr response;
|
||||
if (headersOnly)
|
||||
{
|
||||
response = httpClient.head(url, args);
|
||||
@ -133,21 +133,21 @@ namespace ix
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
for (auto it : response.headers)
|
||||
for (auto it : response->headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Upload size: " << response.uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response.downloadSize << std::endl;
|
||||
std::cerr << "Upload size: " << response->uploadSize << std::endl;
|
||||
std::cerr << "Download size: " << response->downloadSize << std::endl;
|
||||
|
||||
std::cerr << "Status: " << response.statusCode << std::endl;
|
||||
if (response.errorCode != HttpErrorCode::Ok)
|
||||
std::cerr << "Status: " << response->statusCode << std::endl;
|
||||
if (response->errorCode != HttpErrorCode::Ok)
|
||||
{
|
||||
std::cerr << "error message: " << response.errorMsg << std::endl;
|
||||
std::cerr << "error message: " << response->errorMsg << std::endl;
|
||||
}
|
||||
|
||||
if (!headersOnly && response.errorCode == HttpErrorCode::Ok)
|
||||
if (!headersOnly && response->errorCode == HttpErrorCode::Ok)
|
||||
{
|
||||
if (save || !output.empty())
|
||||
{
|
||||
@ -160,14 +160,14 @@ namespace ix
|
||||
|
||||
std::cout << "Writing to disk: " << filename << std::endl;
|
||||
std::ofstream out(filename);
|
||||
out.write((char*)&response.payload.front(), response.payload.size());
|
||||
out.write((char*)&response->payload.front(), response->payload.size());
|
||||
out.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (response.headers["Content-Type"] != "application/octet-stream")
|
||||
if (response->headers["Content-Type"] != "application/octet-stream")
|
||||
{
|
||||
std::cout << "payload: " << response.payload << std::endl;
|
||||
std::cout << "payload: " << response->payload << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -54,59 +54,54 @@ namespace ix
|
||||
log(std::string("Connecting to url: ") + _url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
||||
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
|
||||
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
log("ping_pong: connected");
|
||||
|
||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cout << "Handshake Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "ping_pong: disconnected:"
|
||||
<< " code " << closeInfo.code
|
||||
<< " reason " << closeInfo.reason
|
||||
<< str;
|
||||
<< " code " << msg->closeInfo.code
|
||||
<< " reason " << msg->closeInfo.reason
|
||||
<< msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
ss << "ping_pong: received message: "
|
||||
<< str;
|
||||
<< msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Ping)
|
||||
else if (msg->type == ix::WebSocketMessageType::Ping)
|
||||
{
|
||||
ss << "ping_pong: received ping message: "
|
||||
<< str;
|
||||
<< msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Pong)
|
||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
||||
{
|
||||
ss << "ping_pong: received pong message: "
|
||||
<< str;
|
||||
<< msg->str;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else
|
||||
|
@ -183,41 +183,36 @@ namespace ix
|
||||
log(std::string("Connecting to url: ") + _url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const ix::WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
_condition.notify_one();
|
||||
|
||||
log("ws_receive: connected");
|
||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cout << "Handshake Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "ws_receive: connection closed:";
|
||||
ss << " code " << closeInfo.code;
|
||||
ss << " reason " << closeInfo.reason << std::endl;
|
||||
ss << " code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
ss << "ws_receive: transfered " << wireSize << " bytes";
|
||||
ss << "ws_receive: transfered " << msg->wireSize << " bytes";
|
||||
log(ss.str());
|
||||
handleMessage(str);
|
||||
handleMessage(msg->str);
|
||||
_condition.notify_one();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
ss << "ws_receive: received fragment " << _receivedFragmentCounter++;
|
||||
log(ss.str());
|
||||
@ -229,13 +224,13 @@ namespace ix
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "ws_receive ";
|
||||
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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else
|
||||
|
@ -112,42 +112,37 @@ namespace ix
|
||||
log(std::string("Connecting to url: ") + _url);
|
||||
|
||||
_webSocket.setOnMessageCallback(
|
||||
[this](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[this](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
_condition.notify_one();
|
||||
|
||||
log("ws_send: connected");
|
||||
std::cout << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cout << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cout << "Handshake Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cout << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
ss << "ws_send: connection closed:";
|
||||
ss << " code " << closeInfo.code;
|
||||
ss << " reason " << closeInfo.reason << std::endl;
|
||||
ss << " code " << msg->closeInfo.code;
|
||||
ss << " reason " << msg->closeInfo.reason << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
_condition.notify_one();
|
||||
|
||||
ss << "ws_send: received message (" << wireSize << " bytes)";
|
||||
ss << "ws_send: received message (" << msg->wireSize << " bytes)";
|
||||
log(ss.str());
|
||||
|
||||
std::string errMsg;
|
||||
MsgPack data = MsgPack::parse(str, errMsg);
|
||||
MsgPack data = MsgPack::parse(msg->str, errMsg);
|
||||
if (!errMsg.empty())
|
||||
{
|
||||
std::cerr << "Invalid MsgPack response" << std::endl;
|
||||
@ -160,13 +155,13 @@ namespace ix
|
||||
std::cerr << "Invalid id" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
ss << "ws_send ";
|
||||
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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
log(ss.str());
|
||||
}
|
||||
else
|
||||
@ -244,8 +239,8 @@ namespace ix
|
||||
MsgPack msg(pdu);
|
||||
|
||||
Bench bench("Sending file through websocket");
|
||||
_webSocket.send(msg.dump(),
|
||||
[throttle](int current, int total) -> bool
|
||||
_webSocket.sendBinary(msg.dump(),
|
||||
[throttle](int current, int total) -> bool
|
||||
{
|
||||
std::cout << "ws_send: Step " << current << " out of " << total << std::endl;
|
||||
|
||||
|
@ -21,52 +21,48 @@ namespace ix
|
||||
std::shared_ptr<ConnectionState> connectionState)
|
||||
{
|
||||
webSocket->setOnMessageCallback(
|
||||
[webSocket, connectionState, &server](ix::WebSocketMessageType messageType,
|
||||
const std::string& str,
|
||||
size_t wireSize,
|
||||
const ix::WebSocketErrorInfo& error,
|
||||
const ix::WebSocketOpenInfo& openInfo,
|
||||
const ix::WebSocketCloseInfo& closeInfo)
|
||||
[webSocket, connectionState, &server](const WebSocketMessagePtr& msg)
|
||||
{
|
||||
if (messageType == ix::WebSocketMessageType::Open)
|
||||
if (msg->type == ix::WebSocketMessageType::Open)
|
||||
{
|
||||
std::cerr << "New connection" << std::endl;
|
||||
std::cerr << "id: " << connectionState->getId() << std::endl;
|
||||
std::cerr << "Uri: " << openInfo.uri << std::endl;
|
||||
std::cerr << "Uri: " << msg->openInfo.uri << std::endl;
|
||||
std::cerr << "Headers:" << std::endl;
|
||||
for (auto it : openInfo.headers)
|
||||
for (auto it : msg->openInfo.headers)
|
||||
{
|
||||
std::cerr << it.first << ": " << it.second << std::endl;
|
||||
}
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Close)
|
||||
else if (msg->type == ix::WebSocketMessageType::Close)
|
||||
{
|
||||
std::cerr << "Closed connection"
|
||||
<< " code " << closeInfo.code
|
||||
<< " reason " << closeInfo.reason << std::endl;
|
||||
<< " code " << msg->closeInfo.code
|
||||
<< " reason " << msg->closeInfo.reason << std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Error)
|
||||
else if (msg->type == ix::WebSocketMessageType::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;
|
||||
ss << "Connection error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
std::cerr << ss.str();
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Fragment)
|
||||
else if (msg->type == ix::WebSocketMessageType::Fragment)
|
||||
{
|
||||
std::cerr << "Received message fragment "
|
||||
<< std::endl;
|
||||
}
|
||||
else if (messageType == ix::WebSocketMessageType::Message)
|
||||
else if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
std::cerr << "Received " << wireSize << " bytes" << std::endl;
|
||||
std::cerr << "Received " << msg->wireSize << " bytes" << std::endl;
|
||||
for (auto&& client : server.getClients())
|
||||
{
|
||||
if (client != webSocket)
|
||||
{
|
||||
client->send(str,
|
||||
client->send(msg->str,
|
||||
msg->binary,
|
||||
[](int current, int total) -> bool
|
||||
{
|
||||
std::cerr << "ws_transfer: Step " << current
|
||||
|
Reference in New Issue
Block a user