libwebsockets/minimal-examples-lowlevel/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c

283 lines
7.6 KiB
C

/*
* ws protocol handler plugin for "lws-minimal-pmd-bulk"
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The protocol shows how to send and receive bulk messages over a ws connection
* that optionally may have the permessage-deflate extension negotiated on it.
*/
#if !defined (LWS_PLUGIN_STATIC)
#define LWS_DLL
#define LWS_INTERNAL
#include <libwebsockets.h>
#endif
#include <string.h>
/*
* We will produce a large ws message either from this text repeated many times,
* or from 0x40 + a 6-bit pseudorandom number
*/
static const char * const redundant_string =
"No one would have believed in the last years of the nineteenth "
"century that this world was being watched keenly and closely by "
"intelligences greater than man's and yet as mortal as his own; that as "
"men busied themselves about their various concerns they were "
"scrutinised and studied, perhaps almost as narrowly as a man with a "
"microscope might scrutinise the transient creatures that swarm and "
"multiply in a drop of water. With infinite complacency men went to "
"and fro over this globe about their little affairs, serene in their "
"assurance of their empire over matter. It is possible that the "
"infusoria under the microscope do the same. No one gave a thought to "
"the older worlds of space as sources of human danger, or thought of "
"them only to dismiss the idea of life upon them as impossible or "
"improbable. It is curious to recall some of the mental habits of "
"those departed days. At most terrestrial men fancied there might be "
"other men upon Mars, perhaps inferior to themselves and ready to "
"welcome a missionary enterprise. Yet across the gulf of space, minds "
"that are to our minds as ours are to those of the beasts that perish, "
"intellects vast and cool and unsympathetic, regarded this earth with "
"envious eyes, and slowly and surely drew their plans against us. And "
"early in the twentieth century came the great disillusionment. "
;
/* this reflects the length of the string above */
#define REPEAT_STRING_LEN 1337
/* this is the total size of the ws message we will send */
#define MESSAGE_SIZE (100 * REPEAT_STRING_LEN)
/* this is how much we will send each time the connection is writable */
#define MESSAGE_CHUNK_SIZE (1 * 1024)
/* one of these is created for each client connecting to us */
struct per_session_data__minimal_pmd_bulk {
int position_tx, position_rx;
uint64_t rng_rx, rng_tx;
};
struct vhd_minimal_pmd_bulk {
struct lws_context *context;
struct lws_vhost *vhost;
struct lws *client_wsi;
lws_sorted_usec_list_t sul;
int *interrupted;
int *options;
};
static uint64_t rng(uint64_t *r)
{
*r ^= *r << 21;
*r ^= *r >> 35;
*r ^= *r << 4;
return *r;
}
static void
sul_connect_attempt(struct lws_sorted_usec_list *sul)
{
struct vhd_minimal_pmd_bulk *vhd =
lws_container_of(sul, struct vhd_minimal_pmd_bulk, sul);
struct lws_client_connect_info i;
memset(&i, 0, sizeof(i));
i.context = vhd->context;
i.port = 7681;
i.address = "localhost";
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.ssl_connection = 0;
i.vhost = vhd->vhost;
i.protocol = "lws-minimal-pmd-bulk";
i.pwsi = &vhd->client_wsi;
if (!lws_client_connect_via_info(&i))
lws_sul_schedule(vhd->context, 0, &vhd->sul,
sul_connect_attempt, 10 * LWS_US_PER_SEC);
}
static int
callback_minimal_pmd_bulk(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__minimal_pmd_bulk *pss =
(struct per_session_data__minimal_pmd_bulk *)user;
struct vhd_minimal_pmd_bulk *vhd = (struct vhd_minimal_pmd_bulk *)
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
lws_get_protocol(wsi));
uint8_t buf[LWS_PRE + MESSAGE_CHUNK_SIZE], *start = &buf[LWS_PRE], *p;
int n, m, flags;
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi),
sizeof(struct vhd_minimal_pmd_bulk));
if (!vhd)
return -1;
vhd->context = lws_get_context(wsi);
vhd->vhost = lws_get_vhost(wsi);
/* get the pointer to "interrupted" we were passed in pvo */
vhd->interrupted = (int *)lws_pvo_search(
(const struct lws_protocol_vhost_options *)in,
"interrupted")->value;
vhd->options = (int *)lws_pvo_search(
(const struct lws_protocol_vhost_options *)in,
"options")->value;
sul_connect_attempt(&vhd->sul);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
lws_sul_cancel(&vhd->sul);
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
pss->rng_tx = 4;
pss->rng_rx = 4;
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
/*
* when we connect, we will send the server a message
*/
if (pss->position_tx == MESSAGE_SIZE)
break;
/* fill up one chunk's worth of message content */
p = start;
n = MESSAGE_CHUNK_SIZE;
if (n > MESSAGE_SIZE - pss->position_tx)
n = MESSAGE_SIZE - pss->position_tx;
flags = lws_write_ws_flags(LWS_WRITE_BINARY, !pss->position_tx,
pss->position_tx + n == MESSAGE_SIZE);
/*
* select between producing compressible repeated text,
* or uncompressible PRNG output
*/
if (*vhd->options & 1) {
while (n) {
size_t s;
m = pss->position_tx % REPEAT_STRING_LEN;
s = (unsigned int)(REPEAT_STRING_LEN - m);
if (s > (size_t)n)
s = (unsigned int)n;
memcpy(p, &redundant_string[m], s);
pss->position_tx += (int)s;
p += s;
n -= (int)s;
}
} else {
pss->position_tx += n;
while (n--)
*p++ = (uint8_t)rng(&pss->rng_tx);
}
n = lws_ptr_diff(p, start);
m = lws_write(wsi, start, (unsigned int)n, (enum lws_write_protocol)flags);
if (m < n) {
lwsl_err("ERROR %d writing ws\n", m);
return -1;
}
if (pss->position_tx != MESSAGE_SIZE) /* if more to do... */
lws_callback_on_writable(wsi);
else
/* if we sent and received everything */
if (pss->position_rx == MESSAGE_SIZE)
*vhd->interrupted = 2;
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
/*
* When we connect, the server will send us a message too
*/
lwsl_user("LWS_CALLBACK_CLIENT_RECEIVE: %4d (rpp %5d, last %d)\n",
(int)len, (int)lws_remaining_packet_payload(wsi),
lws_is_final_fragment(wsi));
if (*vhd->options & 1) {
while (len) {
size_t s;
m = pss->position_rx % REPEAT_STRING_LEN;
s = (unsigned int)(REPEAT_STRING_LEN - m);
if (s > len)
s = len;
if (memcmp(in, &redundant_string[m], s)) {
lwsl_user("echo'd data doesn't match\n");
return -1;
}
pss->position_rx += (int)s;
in = ((unsigned char *)in) + s;
len -= s;
}
} else {
p = (uint8_t *)in;
pss->position_rx += (int)len;
while (len--)
if (*p++ != (uint8_t)rng(&pss->rng_rx)) {
lwsl_user("echo'd data doesn't match\n");
return -1;
}
}
/* if we sent and received everything */
if (pss->position_rx == MESSAGE_SIZE &&
pss->position_tx == MESSAGE_SIZE)
*vhd->interrupted = 2;
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
in ? (char *)in : "(null)");
vhd->client_wsi = NULL;
lws_sul_schedule(vhd->context, 0, &vhd->sul,
sul_connect_attempt, LWS_US_PER_SEC);
break;
case LWS_CALLBACK_CLIENT_CLOSED:
vhd->client_wsi = NULL;
lws_sul_schedule(vhd->context, 0, &vhd->sul,
sul_connect_attempt, LWS_US_PER_SEC);
break;
default:
break;
}
return 0;
}
#define LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK \
{ \
"lws-minimal-pmd-bulk", \
callback_minimal_pmd_bulk, \
sizeof(struct per_session_data__minimal_pmd_bulk), \
4096, \
0, NULL, 0 \
}