Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af1a54f2ad | ||
|
|
8a385449ce | ||
|
|
396a6985ae | ||
|
|
92db53c470 | ||
|
|
49865fed0a | ||
|
|
f3f71314d9 | ||
|
|
2d593dd63b | ||
|
|
779b1e6077 |
@@ -1 +1 @@
|
|||||||
7.4.0
|
7.4.3
|
||||||
|
|||||||
@@ -55,5 +55,13 @@ services:
|
|||||||
# networks:
|
# networks:
|
||||||
# - ws-net
|
# - ws-net
|
||||||
|
|
||||||
|
# compile:
|
||||||
|
# image: alpine
|
||||||
|
# entrypoint: sh
|
||||||
|
# stdin_open: true
|
||||||
|
# tty: true
|
||||||
|
# volumes:
|
||||||
|
# - /Users/bsergeant/src/foss:/home/bsergean/src/foss
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
ws-net:
|
ws-net:
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [7.4.3] - 2019-12-03
|
||||||
|
|
||||||
|
- (http client) use std::unordered_map instead of std::map for HttpParameters and HttpFormDataParameters class aliases
|
||||||
|
|
||||||
|
## [7.4.2] - 2019-12-02
|
||||||
|
|
||||||
|
- (client) internal IXDNSLookup class requires a valid cancellation request function callback to be passed in
|
||||||
|
|
||||||
|
## [7.4.1] - 2019-12-02
|
||||||
|
|
||||||
|
- (client) fix an overflow in the exponential back off code
|
||||||
|
|
||||||
## [7.4.0] - 2019-11-25
|
## [7.4.0] - 2019-11-25
|
||||||
|
|
||||||
- (http client) Add support for multipart HTTP POST upload
|
- (http client) Add support for multipart HTTP POST upload
|
||||||
|
|||||||
81
docs/cobra.md
Normal file
81
docs/cobra.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
## General
|
||||||
|
|
||||||
|
[cobra](https://github.com/machinezone/cobra) is a real time messaging server. The `ws` utility can run a cobra server (named snake), and has client to publish and subscribe to a cobra server.
|
||||||
|
|
||||||
|
Bring up 3 terminals and run a server, a publisher and a subscriber in each one. As you publish data you should see it being received by the subscriber. You can run `redis-cli MONITOR` too to see how redis is being used.
|
||||||
|
|
||||||
|
### Server
|
||||||
|
|
||||||
|
You will need to have a redis server running locally. To run the server:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd <ixwebsocket-top-level-folder>/ixsnake/ixsnake
|
||||||
|
$ ws snake
|
||||||
|
{
|
||||||
|
"apps": {
|
||||||
|
"FC2F10139A2BAc53BB72D9db967b024f": {
|
||||||
|
"roles": {
|
||||||
|
"_sub": {
|
||||||
|
"secret": "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba"
|
||||||
|
},
|
||||||
|
"_pub": {
|
||||||
|
"secret": "1c04DB8fFe76A4EeFE3E318C72d771db"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redis host: 127.0.0.1
|
||||||
|
redis password:
|
||||||
|
redis port: 6379
|
||||||
|
```
|
||||||
|
|
||||||
|
### Publisher
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd <ixwebsocket-top-level-folder>/ws
|
||||||
|
$ ws cobra_publish --appkey FC2F10139A2BAc53BB72D9db967b024f --endpoint ws://127.0.0.1:8008 --rolename _pub --rolesecret 1c04DB8fFe76A4EeFE3E318C72d771db test_channel cobraMetricsSample.json
|
||||||
|
[2019-11-27 09:06:12.980] [info] Publisher connected
|
||||||
|
[2019-11-27 09:06:12.980] [info] Connection: Upgrade
|
||||||
|
[2019-11-27 09:06:12.980] [info] Sec-WebSocket-Accept: zTtQKMKbvwjdivURplYXwCVUCWM=
|
||||||
|
[2019-11-27 09:06:12.980] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
|
||||||
|
[2019-11-27 09:06:12.980] [info] Server: ixwebsocket/7.4.0 macos ssl/DarwinSSL zlib 1.2.11
|
||||||
|
[2019-11-27 09:06:12.980] [info] Upgrade: websocket
|
||||||
|
[2019-11-27 09:06:12.982] [info] Publisher authenticated
|
||||||
|
[2019-11-27 09:06:12.982] [info] Published msg 3
|
||||||
|
[2019-11-27 09:06:12.982] [info] Published message id 3 acked
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subscriber
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ws cobra_subscribe --appkey FC2F10139A2BAc53BB72D9db967b024f --endpoint ws://127.0.0.1:8008 --rolename _pub --rolesecret 1c04DB8fFe76A4EeFE3E318C72d771db test_channel
|
||||||
|
#messages 0 msg/s 0
|
||||||
|
[2019-11-27 09:07:39.341] [info] Subscriber connected
|
||||||
|
[2019-11-27 09:07:39.341] [info] Connection: Upgrade
|
||||||
|
[2019-11-27 09:07:39.341] [info] Sec-WebSocket-Accept: 9vkQWofz49qMCUlTSptCCwHWm+Q=
|
||||||
|
[2019-11-27 09:07:39.341] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15
|
||||||
|
[2019-11-27 09:07:39.341] [info] Server: ixwebsocket/7.4.0 macos ssl/DarwinSSL zlib 1.2.11
|
||||||
|
[2019-11-27 09:07:39.341] [info] Upgrade: websocket
|
||||||
|
[2019-11-27 09:07:39.342] [info] Subscriber authenticated
|
||||||
|
[2019-11-27 09:07:39.345] [info] Subscriber: subscribed to channel test_channel
|
||||||
|
#messages 0 msg/s 0
|
||||||
|
#messages 0 msg/s 0
|
||||||
|
#messages 0 msg/s 0
|
||||||
|
{"baz":123,"foo":"bar"}
|
||||||
|
|
||||||
|
#messages 1 msg/s 1
|
||||||
|
#messages 1 msg/s 0
|
||||||
|
#messages 1 msg/s 0
|
||||||
|
{"baz":123,"foo":"bar"}
|
||||||
|
|
||||||
|
{"baz":123,"foo":"bar"}
|
||||||
|
|
||||||
|
#messages 3 msg/s 2
|
||||||
|
#messages 3 msg/s 0
|
||||||
|
{"baz":123,"foo":"bar"}
|
||||||
|
|
||||||
|
#messages 4 msg/s 1
|
||||||
|
^C
|
||||||
|
```
|
||||||
@@ -30,7 +30,7 @@ The regression test is running after each commit on travis.
|
|||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
* On Windows TLS is not setup yet to validate certificates.
|
* On Windows and Android certificate validation needs to be setup so that SocketTLSOptions.caFile point to a pem file, such as the one distributed by Firefox. Unless that setup is done connecting to a wss endpoint will display an error. On Windows with mbedtls the message will contain `error in handshake : X509 - Certificate verification failed, e.g. CRL, CA or signature check failed`.
|
||||||
* There is no convenient way to embed a ca cert.
|
* There is no convenient way to embed a ca cert.
|
||||||
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
|
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
|
||||||
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
|
* The server code is using select to detect incoming data, and creates one OS thread per connection. This is not as scalable as strategies using epoll or kqueue.
|
||||||
|
|||||||
@@ -29,8 +29,13 @@ namespace ix
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CancellationRequest cancellationRequest = []() -> bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
return _socket->connect(hostname, port, errMsg, nullptr);
|
return _socket->connect(hostname, port, errMsg, cancellationRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RedisClient::stop()
|
void RedisClient::stop()
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace ix
|
|||||||
errMsg = "no error";
|
errMsg = "no error";
|
||||||
|
|
||||||
// Maybe a cancellation request got in before the background thread terminated ?
|
// Maybe a cancellation request got in before the background thread terminated ?
|
||||||
if (isCancellationRequested && isCancellationRequested())
|
if (isCancellationRequested())
|
||||||
{
|
{
|
||||||
errMsg = "cancellation requested";
|
errMsg = "cancellation requested";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -107,7 +107,7 @@ namespace ix
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(_wait));
|
std::this_thread::sleep_for(std::chrono::milliseconds(_wait));
|
||||||
|
|
||||||
// Were we cancelled ?
|
// Were we cancelled ?
|
||||||
if (isCancellationRequested && isCancellationRequested())
|
if (isCancellationRequested())
|
||||||
{
|
{
|
||||||
errMsg = "cancellation requested";
|
errMsg = "cancellation requested";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -115,7 +115,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Maybe a cancellation request got in before the bg terminated ?
|
// Maybe a cancellation request got in before the bg terminated ?
|
||||||
if (isCancellationRequested && isCancellationRequested())
|
if (isCancellationRequested())
|
||||||
{
|
{
|
||||||
errMsg = "cancellation requested";
|
errMsg = "cancellation requested";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace ix
|
|||||||
uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
|
uint32_t calculateRetryWaitMilliseconds(uint32_t retry_count,
|
||||||
uint32_t maxWaitBetweenReconnectionRetries)
|
uint32_t maxWaitBetweenReconnectionRetries)
|
||||||
{
|
{
|
||||||
uint32_t wait_time = std::pow(2, retry_count) * 100;
|
uint32_t wait_time = (retry_count < 26) ? (std::pow(2, retry_count) * 100) : 0;
|
||||||
|
|
||||||
if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0)
|
if (wait_time > maxWaitBetweenReconnectionRetries || wait_time == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "IXProgressCallback.h"
|
#include "IXProgressCallback.h"
|
||||||
#include "IXWebSocketHttpHeaders.h"
|
#include "IXWebSocketHttpHeaders.h"
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
@@ -65,8 +66,8 @@ namespace ix
|
|||||||
};
|
};
|
||||||
|
|
||||||
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
|
using HttpResponsePtr = std::shared_ptr<HttpResponse>;
|
||||||
using HttpParameters = std::map<std::string, std::string>;
|
using HttpParameters = std::unordered_map<std::string, std::string>;
|
||||||
using HttpFormDataParameters = std::map<std::string, std::string>;
|
using HttpFormDataParameters = std::unordered_map<std::string, std::string>;
|
||||||
using Logger = std::function<void(const std::string&)>;
|
using Logger = std::function<void(const std::string&)>;
|
||||||
using OnResponseCallback = std::function<void(const HttpResponsePtr&)>;
|
using OnResponseCallback = std::function<void(const HttpResponsePtr&)>;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
*
|
*
|
||||||
* This is the right example to look at:
|
* This is the right example to look at:
|
||||||
* https://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL
|
* https://www.codeproject.com/Articles/1000189/A-Working-TCP-Client-and-Server-With-SSL
|
||||||
|
*
|
||||||
|
* Similar code is available from this git repo
|
||||||
|
* https://github.com/david-maw/StreamSSL
|
||||||
*/
|
*/
|
||||||
#include "IXSocketSChannel.h"
|
#include "IXSocketSChannel.h"
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IX_WEBSOCKET_VERSION "7.4.0"
|
#define IX_WEBSOCKET_VERSION "7.4.3"
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ int main(int argc, char** argv)
|
|||||||
cobraSubscribeApp->add_option("--endpoint", endpoint, "Endpoint")->required();
|
cobraSubscribeApp->add_option("--endpoint", endpoint, "Endpoint")->required();
|
||||||
cobraSubscribeApp->add_option("--rolename", rolename, "Role name")->required();
|
cobraSubscribeApp->add_option("--rolename", rolename, "Role name")->required();
|
||||||
cobraSubscribeApp->add_option("--rolesecret", rolesecret, "Role secret")->required();
|
cobraSubscribeApp->add_option("--rolesecret", rolesecret, "Role secret")->required();
|
||||||
cobraSubscribeApp->add_option("channel", channel, "Channel")->required();
|
cobraSubscribeApp->add_option("--channel", channel, "Channel")->required();
|
||||||
cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
|
cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file");
|
||||||
cobraSubscribeApp->add_option("--filter", filter, "Stream SQL Filter");
|
cobraSubscribeApp->add_option("--filter", filter, "Stream SQL Filter");
|
||||||
cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats");
|
cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats");
|
||||||
@@ -224,7 +224,7 @@ int main(int argc, char** argv)
|
|||||||
cobraPublish->add_option("--endpoint", endpoint, "Endpoint")->required();
|
cobraPublish->add_option("--endpoint", endpoint, "Endpoint")->required();
|
||||||
cobraPublish->add_option("--rolename", rolename, "Role name")->required();
|
cobraPublish->add_option("--rolename", rolename, "Role name")->required();
|
||||||
cobraPublish->add_option("--rolesecret", rolesecret, "Role secret")->required();
|
cobraPublish->add_option("--rolesecret", rolesecret, "Role secret")->required();
|
||||||
cobraPublish->add_option("channel", channel, "Channel")->required();
|
cobraPublish->add_option("--channel", channel, "Channel")->required();
|
||||||
cobraPublish->add_option("--pidfile", pidfile, "Pid file");
|
cobraPublish->add_option("--pidfile", pidfile, "Pid file");
|
||||||
cobraPublish->add_option("path", path, "Path to the file to send")
|
cobraPublish->add_option("path", path, "Path to the file to send")
|
||||||
->required()
|
->required()
|
||||||
@@ -236,7 +236,7 @@ int main(int argc, char** argv)
|
|||||||
cobraMetricsPublish->add_option("--endpoint", endpoint, "Endpoint");
|
cobraMetricsPublish->add_option("--endpoint", endpoint, "Endpoint");
|
||||||
cobraMetricsPublish->add_option("--rolename", rolename, "Role name");
|
cobraMetricsPublish->add_option("--rolename", rolename, "Role name");
|
||||||
cobraMetricsPublish->add_option("--rolesecret", rolesecret, "Role secret");
|
cobraMetricsPublish->add_option("--rolesecret", rolesecret, "Role secret");
|
||||||
cobraMetricsPublish->add_option("channel", channel, "Channel")->required();
|
cobraMetricsPublish->add_option("--channel", channel, "Channel")->required();
|
||||||
cobraMetricsPublish->add_option("--pidfile", pidfile, "Pid file");
|
cobraMetricsPublish->add_option("--pidfile", pidfile, "Pid file");
|
||||||
cobraMetricsPublish->add_option("path", path, "Path to the file to send")
|
cobraMetricsPublish->add_option("path", path, "Path to the file to send")
|
||||||
->required()
|
->required()
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace ix
|
|||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
std::chrono::duration<double, std::milli> duration(10);
|
auto duration = std::chrono::seconds(1);
|
||||||
std::this_thread::sleep_for(duration);
|
std::this_thread::sleep_for(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user