2015-12-04 01:47:49 +01:00
|
|
|
/** Node type: Websockets (libwebsockets)
|
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2017-03-03 20:20:13 -04:00
|
|
|
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
2015-12-04 01:47:49 +01:00
|
|
|
*********************************************************************************/
|
|
|
|
|
2015-12-02 13:55:58 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <signal.h>
|
2015-12-13 02:01:57 +01:00
|
|
|
|
2015-12-11 12:35:32 +01:00
|
|
|
#include <libconfig.h>
|
2015-12-02 13:55:58 +01:00
|
|
|
|
2017-03-12 17:01:24 -03:00
|
|
|
#include "super_node.h"
|
2016-11-07 22:19:30 -05:00
|
|
|
#include "webmsg_format.h"
|
2015-12-02 13:55:58 +01:00
|
|
|
#include "timing.h"
|
|
|
|
#include "utils.h"
|
2016-07-11 18:18:20 +02:00
|
|
|
#include "config.h"
|
2017-03-06 12:28:06 -04:00
|
|
|
#include "plugin.h"
|
2015-12-04 01:47:49 +01:00
|
|
|
|
2017-03-12 17:13:37 -03:00
|
|
|
#include "nodes/websocket.h"
|
|
|
|
|
2015-12-13 02:01:57 +01:00
|
|
|
/* Private static storage */
|
2017-03-06 12:28:06 -04:00
|
|
|
struct list connections; /**< List of active libwebsocket connections which receive samples from all nodes (catch all) */
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
/* Forward declarations */
|
2017-03-12 17:13:37 -03:00
|
|
|
static struct plugin p;
|
2015-12-04 01:47:49 +01:00
|
|
|
|
2017-03-06 12:28:06 -04:00
|
|
|
static char * websocket_connection_name(struct websocket_connection *c)
|
2016-11-08 00:24:57 -05:00
|
|
|
{
|
|
|
|
if (!c->_name) {
|
|
|
|
if (c->node)
|
|
|
|
asprintf(&c->_name, "%s (%s) for node %s", c->peer.name, c->peer.ip, node_name(c->node));
|
|
|
|
else
|
|
|
|
asprintf(&c->_name, "%s (%s) for all nodes", c->peer.name, c->peer.ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
return c->_name;
|
|
|
|
}
|
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
static int websocket_connection_init(struct websocket_connection *c, struct lws *wsi)
|
2017-03-16 22:42:58 -03:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2017-04-02 13:00:34 +02:00
|
|
|
struct websocket *w = c->node->_vd;
|
2017-03-16 22:42:58 -03:00
|
|
|
|
|
|
|
lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), c->peer.name, sizeof(c->peer.name), c->peer.ip, sizeof(c->peer.ip));
|
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
info("LWS: New connection %s", websocket_connection_name(c));
|
2017-03-16 22:42:58 -03:00
|
|
|
|
2017-04-02 13:02:07 +02:00
|
|
|
c->state = STATE_INITIALIZED;
|
2017-03-16 22:42:58 -03:00
|
|
|
c->wsi = wsi;
|
|
|
|
|
|
|
|
if (c->node != NULL)
|
|
|
|
list_push(&w->connections, c);
|
|
|
|
else
|
|
|
|
list_push(&connections, c);
|
|
|
|
|
|
|
|
ret = queue_init(&c->queue, DEFAULT_QUEUELEN, &memtype_hugepage);
|
|
|
|
if (ret) {
|
|
|
|
warn("Failed to create queue for incoming websocket connection. Closing..");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
static void websocket_connection_destroy(struct websocket_connection *c)
|
2017-03-16 22:42:58 -03:00
|
|
|
{
|
2017-04-02 13:02:07 +02:00
|
|
|
if (c->state == STATE_DESTROYED)
|
|
|
|
return;
|
|
|
|
|
2017-04-02 13:00:34 +02:00
|
|
|
struct websocket *w = c->node->_vd;
|
2017-04-02 13:02:07 +02:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
info("LWS: Connection %s closed", websocket_connection_name(c));
|
2017-04-02 13:02:07 +02:00
|
|
|
|
|
|
|
c->state = STATE_DESTROYED;
|
2017-03-16 22:42:58 -03:00
|
|
|
c->wsi = NULL;
|
|
|
|
|
|
|
|
if (c->node)
|
|
|
|
list_remove(&w->connections, c);
|
|
|
|
else
|
|
|
|
list_remove(&connections, c);
|
|
|
|
|
|
|
|
if (c->_name)
|
|
|
|
free(c->_name);
|
|
|
|
|
|
|
|
queue_destroy(&c->queue);
|
|
|
|
}
|
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
static void websocket_destination_destroy(struct websocket_destination *d)
|
2016-11-08 00:24:57 -05:00
|
|
|
{
|
|
|
|
free(d->uri);
|
|
|
|
}
|
|
|
|
|
2017-03-06 12:28:06 -04:00
|
|
|
static int websocket_connection_write(struct websocket_connection *c, struct sample *smps[], unsigned cnt)
|
2016-11-08 00:24:57 -05:00
|
|
|
{
|
|
|
|
int blocks, enqueued;
|
|
|
|
char *bufs[cnt];
|
|
|
|
|
|
|
|
struct websocket *w = c->node->_vd;
|
|
|
|
|
|
|
|
switch (c->state) {
|
2017-04-02 13:02:07 +02:00
|
|
|
case STATE_DESTROYED:
|
|
|
|
case STATE_STOPPED:
|
2016-11-08 00:24:57 -05:00
|
|
|
return -1;
|
2017-03-29 06:01:50 +02:00
|
|
|
|
2017-04-02 13:02:07 +02:00
|
|
|
case STATE_INITIALIZED:
|
|
|
|
c->state = STATE_STARTED;
|
2016-11-08 00:24:57 -05:00
|
|
|
/* fall through */
|
|
|
|
|
2017-04-02 13:02:07 +02:00
|
|
|
case STATE_STARTED:
|
2016-11-08 00:24:57 -05:00
|
|
|
blocks = pool_get_many(&w->pool, (void **) bufs, cnt);
|
|
|
|
if (blocks != cnt)
|
2017-03-06 12:28:06 -04:00
|
|
|
warn("Pool underrun in websocket connection: %s", websocket_connection_name(c));
|
2016-11-08 00:24:57 -05:00
|
|
|
|
|
|
|
for (int i = 0; i < blocks; i++) {
|
|
|
|
struct webmsg *msg = (struct webmsg *) (bufs[i] + LWS_PRE);
|
|
|
|
|
|
|
|
msg->version = WEBMSG_VERSION;
|
|
|
|
msg->type = WEBMSG_TYPE_DATA;
|
|
|
|
msg->endian = WEBMSG_ENDIAN_HOST;
|
|
|
|
msg->length = smps[i]->length;
|
|
|
|
msg->sequence = smps[i]->sequence;
|
2017-04-02 12:59:56 +02:00
|
|
|
msg->id = c->node->id;
|
2016-11-08 00:24:57 -05:00
|
|
|
msg->ts.sec = smps[i]->ts.origin.tv_sec;
|
|
|
|
msg->ts.nsec = smps[i]->ts.origin.tv_nsec;
|
|
|
|
|
|
|
|
memcpy(&msg->data, &smps[i]->data, smps[i]->length * 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
enqueued = queue_push_many(&c->queue, (void **) bufs, cnt);
|
|
|
|
if (enqueued != blocks)
|
2017-03-06 12:28:06 -04:00
|
|
|
warn("Queue overrun in websocket connection: %s", websocket_connection_name(c));
|
2016-11-08 00:24:57 -05:00
|
|
|
|
|
|
|
lws_callback_on_writable(c->wsi);
|
|
|
|
break;
|
2017-04-02 13:02:07 +02:00
|
|
|
|
|
|
|
default: { }
|
2016-11-08 00:24:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-06 12:28:06 -04:00
|
|
|
int websocket_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
|
2015-12-04 01:47:49 +01:00
|
|
|
{
|
2016-11-07 22:19:30 -05:00
|
|
|
int ret;
|
2017-03-06 12:28:06 -04:00
|
|
|
struct websocket_connection *c = user;
|
2015-12-04 01:47:49 +01:00
|
|
|
struct websocket *w;
|
|
|
|
|
2015-12-02 13:55:58 +01:00
|
|
|
switch (reason) {
|
2016-07-11 18:18:20 +02:00
|
|
|
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
|
|
|
case LWS_CALLBACK_ESTABLISHED: {
|
2017-04-02 13:02:07 +02:00
|
|
|
c->state = STATE_DESTROYED;
|
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
/* Get path of incoming request */
|
|
|
|
char uri[64];
|
2015-12-04 01:47:49 +01:00
|
|
|
lws_hdr_copy(wsi, uri, sizeof(uri), WSI_TOKEN_GET_URI); /* The path component of the*/
|
2016-07-11 18:18:20 +02:00
|
|
|
if (strlen(uri) <= 0) {
|
|
|
|
warn("LWS: Closing connection with invalid URL: %s", uri);
|
|
|
|
return -1;
|
2015-12-04 01:47:49 +01:00
|
|
|
}
|
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
if ((uri[0] == '/' && uri[1] == 0) || uri[0] == 0){
|
|
|
|
/* Catch all connection */
|
|
|
|
c->node = NULL;
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
2016-11-07 22:19:30 -05:00
|
|
|
else {
|
|
|
|
char *node = uri + 1;
|
2016-02-04 17:13:28 +01:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
/* Search for node whose name matches the URI. */
|
2017-03-12 17:13:37 -03:00
|
|
|
c->node = list_lookup(&p.node.instances, node);
|
2016-11-07 22:19:30 -05:00
|
|
|
if (c->node == NULL) {
|
|
|
|
warn("LWS: Closing Connection for non-existent node: %s", uri + 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if node is running */
|
2017-03-11 23:50:30 -03:00
|
|
|
if (c->node->state != STATE_STARTED)
|
2016-11-07 22:19:30 -05:00
|
|
|
return -1;
|
|
|
|
}
|
2016-02-04 18:25:13 +01:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
ret = websocket_connection_init(c, wsi);
|
2017-03-16 22:42:58 -03:00
|
|
|
if (ret)
|
2016-11-07 22:19:30 -05:00
|
|
|
return -1;
|
2015-12-04 01:47:49 +01:00
|
|
|
|
|
|
|
return 0;
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
|
|
|
|
2015-12-04 01:47:49 +01:00
|
|
|
case LWS_CALLBACK_CLOSED:
|
2017-03-29 06:01:50 +02:00
|
|
|
websocket_connection_destroy(c);
|
2015-12-04 01:47:49 +01:00
|
|
|
return 0;
|
2015-12-04 17:32:51 +01:00
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
2017-03-29 06:01:50 +02:00
|
|
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
2017-03-27 12:50:01 +02:00
|
|
|
w = c->node->_vd;
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-03-11 23:50:30 -03:00
|
|
|
if (c->node && c->node->state != STATE_STARTED)
|
2016-02-04 18:25:13 +01:00
|
|
|
return -1;
|
|
|
|
|
2017-04-02 13:02:07 +02:00
|
|
|
if (c->state == STATE_STOPPED) {
|
2016-11-07 22:19:30 -05:00
|
|
|
lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY, (unsigned char *) "Node stopped", 4);
|
2016-07-11 18:18:20 +02:00
|
|
|
return -1;
|
2016-02-04 16:30:36 +01:00
|
|
|
}
|
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
char *buf;
|
|
|
|
int cnt;
|
|
|
|
while ((cnt = queue_pull(&c->queue, (void **) &buf))) {
|
|
|
|
struct webmsg *msg = (struct webmsg *) (buf + LWS_PRE);
|
2016-02-04 16:30:36 +01:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
pool_put(&w->pool, (void *) buf);
|
2016-02-04 16:30:36 +01:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
ret = lws_write(wsi, (unsigned char *) msg, WEBMSG_LEN(msg->length), LWS_WRITE_BINARY);
|
|
|
|
if (ret < WEBMSG_LEN(msg->length))
|
2016-07-11 18:18:20 +02:00
|
|
|
error("Failed lws_write()");
|
|
|
|
|
|
|
|
if (lws_send_pipe_choked(wsi))
|
2016-11-07 22:19:30 -05:00
|
|
|
break;
|
2016-02-04 16:30:36 +01:00
|
|
|
}
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
if (queue_available(&c->queue) > 0)
|
2016-07-11 18:18:20 +02:00
|
|
|
lws_callback_on_writable(wsi);
|
2015-12-02 13:55:58 +01:00
|
|
|
|
2015-12-04 01:47:49 +01:00
|
|
|
return 0;
|
2015-12-02 13:55:58 +01:00
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
case LWS_CALLBACK_CLIENT_RECEIVE:
|
|
|
|
case LWS_CALLBACK_RECEIVE: {
|
2017-03-27 12:50:01 +02:00
|
|
|
w = c->node->_vd;
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-03-11 23:50:30 -03:00
|
|
|
if (c->node->state != STATE_STARTED)
|
2015-12-13 20:24:56 +01:00
|
|
|
return -1;
|
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
if (!lws_frame_is_binary(wsi) || len < WEBMSG_LEN(0))
|
2017-03-06 12:28:06 -04:00
|
|
|
warn("LWS: Received invalid packet for connection %s", websocket_connection_name(c));
|
2015-12-13 20:24:56 +01:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
struct webmsg *msg = (struct webmsg *) in;
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
while ((char *) msg + WEBMSG_LEN(msg->length) <= (char *) in + len) {
|
|
|
|
struct webmsg *msg2 = pool_get(&w->pool);
|
2016-07-11 18:18:20 +02:00
|
|
|
if (!msg2) {
|
2017-03-06 12:28:06 -04:00
|
|
|
warn("Pool underrun for connection %s", websocket_connection_name(c));
|
2016-11-07 22:19:30 -05:00
|
|
|
break;
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
memcpy(msg2, msg, WEBMSG_LEN(msg->length));
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-04-07 12:25:17 +02:00
|
|
|
ret = queue_signalled_push_many(&w->queue, (void **) msg2, 1);
|
2016-11-07 22:19:30 -05:00
|
|
|
if (ret != 1) {
|
2017-03-06 12:28:06 -04:00
|
|
|
warn("Queue overrun for connection %s", websocket_connection_name(c));
|
2016-11-07 22:19:30 -05:00
|
|
|
break;
|
|
|
|
}
|
2016-07-11 18:18:20 +02:00
|
|
|
|
|
|
|
/* Next message */
|
2016-11-07 22:19:30 -05:00
|
|
|
msg = (struct webmsg *) ((char *) msg + WEBMSG_LEN(msg->length));
|
2016-01-14 23:17:39 +01:00
|
|
|
}
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2015-12-04 01:47:49 +01:00
|
|
|
return 0;
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
|
|
|
|
2015-12-02 13:55:58 +01:00
|
|
|
default:
|
2015-12-04 01:47:49 +01:00
|
|
|
return 0;
|
2015-12-02 13:55:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-11 23:30:24 -03:00
|
|
|
int websocket_start(struct node *n)
|
2015-12-02 13:55:58 +01:00
|
|
|
{
|
2016-07-11 18:18:20 +02:00
|
|
|
int ret;
|
2016-11-07 22:19:30 -05:00
|
|
|
struct websocket *w = n->_vd;
|
|
|
|
|
|
|
|
size_t blocklen = LWS_PRE + WEBMSG_LEN(DEFAULT_VALUES);
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
ret = pool_init(&w->pool, 64 * DEFAULT_QUEUELEN, blocklen, &memtype_hugepage);
|
2016-07-11 18:18:20 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-04-07 12:25:17 +02:00
|
|
|
ret = queue_signalled_init(&w->queue, DEFAULT_QUEUELEN, &memtype_hugepage);
|
2017-04-02 13:02:49 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2017-03-16 22:42:58 -03:00
|
|
|
/** @todo Connection to destinations via WebSocket client */
|
|
|
|
|
2015-12-02 13:55:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-11 23:30:24 -03:00
|
|
|
int websocket_stop(struct node *n)
|
2016-02-04 17:13:28 +01:00
|
|
|
{
|
2017-04-02 13:02:49 +02:00
|
|
|
int ret;
|
2016-02-04 17:13:28 +01:00
|
|
|
struct websocket *w = n->_vd;
|
|
|
|
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t i = 0; i < list_length(&w->connections); i++) {
|
|
|
|
struct websocket_connection *c = list_at(&w->connections, i);
|
|
|
|
|
2017-04-02 13:02:07 +02:00
|
|
|
c->state = STATE_STOPPED;
|
2017-03-29 06:01:50 +02:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
lws_callback_on_writable(c->wsi);
|
|
|
|
}
|
2016-06-08 22:39:17 +02:00
|
|
|
|
2017-04-02 13:02:49 +02:00
|
|
|
ret = pool_destroy(&w->pool);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-04-07 12:25:17 +02:00
|
|
|
ret = queue_signalled_destroy(&w->queue);
|
2017-04-02 13:02:49 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-02-04 17:13:28 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int websocket_destroy(struct node *n)
|
2015-12-02 13:55:58 +01:00
|
|
|
{
|
2016-11-08 00:24:57 -05:00
|
|
|
struct websocket *w = n->_vd;
|
2017-03-16 22:42:58 -03:00
|
|
|
|
|
|
|
list_destroy(&w->connections, NULL, false);
|
2017-03-06 12:28:06 -04:00
|
|
|
list_destroy(&w->destinations, (dtor_cb_t) websocket_destination_destroy, true);
|
2015-12-04 01:47:49 +01:00
|
|
|
|
2015-12-02 13:55:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
int websocket_read(struct node *n, struct sample *smps[], unsigned cnt)
|
2015-12-02 13:55:58 +01:00
|
|
|
{
|
2017-03-16 22:42:58 -03:00
|
|
|
int got;
|
|
|
|
|
2015-12-04 17:32:51 +01:00
|
|
|
struct websocket *w = n->_vd;
|
2016-11-07 22:19:30 -05:00
|
|
|
struct webmsg *msgs[cnt];
|
2017-03-16 22:42:58 -03:00
|
|
|
|
2017-04-02 13:02:49 +02:00
|
|
|
do {
|
2017-04-07 12:25:17 +02:00
|
|
|
got = queue_signalled_pull_many(&w->queue, (void **) msgs, cnt);
|
2017-04-02 13:02:49 +02:00
|
|
|
if (got < 0)
|
|
|
|
return got;
|
|
|
|
} while (got == 0);
|
|
|
|
|
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
for (int i = 0; i < got; i++) {
|
|
|
|
smps[i]->sequence = msgs[i]->sequence;
|
|
|
|
smps[i]->length = msgs[i]->length;
|
2016-11-07 22:19:30 -05:00
|
|
|
smps[i]->ts.origin = WEBMSG_TS(msgs[i]);
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
memcpy(&smps[i]->data, &msgs[i]->data, WEBMSG_DATA_LEN(msgs[i]->length));
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pool_put_many(&w->pool, (void **) msgs, got);
|
2016-06-08 22:39:17 +02:00
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
return got;
|
2015-12-02 13:55:58 +01:00
|
|
|
}
|
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
int websocket_write(struct node *n, struct sample *smps[], unsigned cnt)
|
2015-12-02 13:55:58 +01:00
|
|
|
{
|
2015-12-04 17:32:51 +01:00
|
|
|
struct websocket *w = n->_vd;
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t i = 0; i < list_length(&w->connections); i++) {
|
|
|
|
struct websocket_connection *c = list_at(&w->connections, i);
|
|
|
|
|
2017-03-06 12:28:06 -04:00
|
|
|
websocket_connection_write(c, smps, cnt);
|
2017-03-25 21:23:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < list_length(&connections); i++) {
|
|
|
|
struct websocket_connection *c = list_at(&connections, i);
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2017-03-06 12:28:06 -04:00
|
|
|
websocket_connection_write(c, smps, cnt);
|
2017-03-25 21:23:31 +01:00
|
|
|
}
|
2016-06-08 22:39:17 +02:00
|
|
|
|
2016-07-11 18:18:20 +02:00
|
|
|
return cnt;
|
2015-12-02 13:55:58 +01:00
|
|
|
}
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
int websocket_parse(struct node *n, config_setting_t *cfg)
|
|
|
|
{
|
2016-11-08 00:24:57 -05:00
|
|
|
struct websocket *w = n->_vd;
|
2016-11-07 22:19:30 -05:00
|
|
|
config_setting_t *cfg_dests;
|
2016-11-08 00:24:57 -05:00
|
|
|
int ret;
|
2017-03-16 22:42:58 -03:00
|
|
|
|
|
|
|
list_init(&w->connections);
|
|
|
|
list_init(&w->destinations);
|
|
|
|
|
2016-11-07 22:19:30 -05:00
|
|
|
cfg_dests = config_setting_get_member(cfg, "destinations");
|
2017-03-11 23:50:46 -03:00
|
|
|
if (cfg_dests) {
|
|
|
|
if (!config_setting_is_array(cfg_dests))
|
|
|
|
cerror(cfg_dests, "The 'destinations' setting must be an array of URLs");
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2017-03-11 23:50:46 -03:00
|
|
|
for (int i = 0; i < config_setting_length(cfg_dests); i++) {
|
|
|
|
const char *uri, *prot, *ads, *path;
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2017-03-11 23:50:46 -03:00
|
|
|
uri = config_setting_get_string_elem(cfg_dests, i);
|
|
|
|
if (!uri)
|
|
|
|
cerror(cfg_dests, "The 'destinations' setting must be an array of URLs");
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
struct websocket_destination d;
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
d.uri = strdup(uri);
|
|
|
|
if (!d.uri)
|
2017-03-11 23:50:46 -03:00
|
|
|
serror("Failed to allocate memory");
|
2016-11-08 00:24:57 -05:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
ret = lws_parse_uri(d.uri, &prot, &ads, &d.info.port, &path);
|
2017-03-11 23:50:46 -03:00
|
|
|
if (ret)
|
|
|
|
cerror(cfg_dests, "Failed to parse websocket URI: '%s'", uri);
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
d.info.ssl_connection = !strcmp(prot, "https");
|
|
|
|
d.info.address = ads;
|
|
|
|
d.info.path = path;
|
|
|
|
d.info.protocol = prot;
|
|
|
|
d.info.ietf_version_or_minus_one = -1;
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-03-29 06:01:50 +02:00
|
|
|
list_push(&w->destinations, memdup(&d, sizeof(d)));
|
2017-03-11 23:50:46 -03:00
|
|
|
}
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
2017-03-08 09:53:28 -03:00
|
|
|
|
2016-11-08 00:24:57 -05:00
|
|
|
return 0;
|
2016-11-07 22:19:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
char * websocket_print(struct node *n)
|
|
|
|
{
|
2016-11-08 00:24:57 -05:00
|
|
|
struct websocket *w = n->_vd;
|
|
|
|
|
|
|
|
char *buf = NULL;
|
2016-07-11 18:18:20 +02:00
|
|
|
|
2017-03-11 23:51:09 -03:00
|
|
|
buf = strcatf(&buf, "dests=");
|
|
|
|
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t i = 0; i < list_length(&w->destinations); i++) {
|
|
|
|
struct lws_client_connect_info *in = list_at(&w->destinations, i);
|
|
|
|
|
2016-11-08 00:24:57 -05:00
|
|
|
buf = strcatf(&buf, "%s://%s:%d/%s",
|
|
|
|
in->ssl_connection ? "https" : "http",
|
|
|
|
in->address,
|
|
|
|
in->port,
|
|
|
|
in->path
|
|
|
|
);
|
2016-07-11 18:18:20 +02:00
|
|
|
}
|
2016-11-07 22:19:30 -05:00
|
|
|
|
2016-11-08 00:24:57 -05:00
|
|
|
return buf;
|
2015-12-02 13:55:58 +01:00
|
|
|
}
|
|
|
|
|
2017-02-18 10:51:43 -05:00
|
|
|
static struct plugin p = {
|
2015-12-04 17:32:51 +01:00
|
|
|
.name = "websocket",
|
|
|
|
.description = "Send and receive samples of a WebSocket connection (libwebsockets)",
|
2017-02-18 10:51:43 -05:00
|
|
|
.type = PLUGIN_TYPE_NODE,
|
|
|
|
.node = {
|
|
|
|
.vectorize = 0, /* unlimited */
|
|
|
|
.size = sizeof(struct websocket),
|
2017-03-11 23:30:24 -03:00
|
|
|
.start = websocket_start,
|
|
|
|
.stop = websocket_stop,
|
2017-02-18 10:51:43 -05:00
|
|
|
.destroy = websocket_destroy,
|
|
|
|
.read = websocket_read,
|
|
|
|
.write = websocket_write,
|
2017-03-05 10:06:32 -04:00
|
|
|
.print = websocket_print,
|
2017-03-12 17:08:52 -03:00
|
|
|
.parse = websocket_parse,
|
|
|
|
.instances = LIST_INIT()
|
2017-02-18 10:51:43 -05:00
|
|
|
}
|
2015-12-04 17:32:51 +01:00
|
|
|
};
|
|
|
|
|
2017-04-07 12:25:17 +02:00
|
|
|
REGISTER_PLUGIN(&p)
|