| /* |
| * ws protocol handler plugin for "lws-minimal-pmd-bulk" |
| * |
| * Copyright (C) 2010-2018 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 { |
| int *interrupted; |
| /* |
| * b0 = 1: test compressible text, = 0: test uncompressible binary |
| * b1 = 1: send as a single blob, = 0: send as fragments |
| */ |
| int *options; |
| }; |
| |
| static uint64_t rng(uint64_t *r) |
| { |
| *r ^= *r << 21; |
| *r ^= *r >> 35; |
| *r ^= *r << 4; |
| |
| return *r; |
| } |
| |
| 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_SIZE], *start = &buf[LWS_PRE], *p; |
| int n, m, flags, olen, amount; |
| |
| 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; |
| |
| /* 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; |
| break; |
| |
| case LWS_CALLBACK_ESTABLISHED: |
| pss->rng_tx = 4; |
| pss->rng_rx = 4; |
| lws_callback_on_writable(wsi); |
| break; |
| |
| case LWS_CALLBACK_SERVER_WRITEABLE: |
| if (pss->position_tx == MESSAGE_SIZE) |
| break; |
| |
| amount = MESSAGE_CHUNK_SIZE; |
| if ((*vhd->options) & 2) { |
| amount = MESSAGE_SIZE; |
| lwsl_user("(writing as one blob of %d)\n", amount); |
| } |
| |
| /* fill up one chunk's worth of message content */ |
| |
| p = start; |
| n = amount; |
| 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 = REPEAT_STRING_LEN - m; |
| if (s > (size_t)n) |
| s = n; |
| memcpy(p, &redundant_string[m], s); |
| pss->position_tx += s; |
| p += s; |
| n -= s; |
| } |
| } else { |
| pss->position_tx += n; |
| while (n--) |
| *p++ = rng(&pss->rng_tx); |
| } |
| |
| n = lws_ptr_diff(p, start); |
| m = lws_write(wsi, start, n, flags); |
| lwsl_user("LWS_CALLBACK_SERVER_WRITEABLE: wrote %d\n", n); |
| if (m < n) { |
| lwsl_err("ERROR %d writing ws\n", n); |
| return -1; |
| } |
| if (pss->position_tx != MESSAGE_SIZE) /* if more to do... */ |
| lws_callback_on_writable(wsi); |
| break; |
| |
| case LWS_CALLBACK_RECEIVE: |
| lwsl_user("LWS_CALLBACK_RECEIVE: %4d (pss->pos=%d, rpp %5d, last %d)\n", |
| (int)len, (int)pss->position_rx, (int)lws_remaining_packet_payload(wsi), |
| lws_is_final_fragment(wsi)); |
| olen = len; |
| |
| if (*vhd->options & 1) { |
| while (len) { |
| size_t s; |
| m = pss->position_rx % REPEAT_STRING_LEN; |
| s = 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 += s; |
| in = ((char *)in) + s; |
| len -= s; |
| } |
| } else { |
| p = (uint8_t *)in; |
| pss->position_rx += len; |
| while (len--) { |
| if (*p++ != (uint8_t)rng(&pss->rng_rx)) { |
| lwsl_user("echo'd data doesn't match: 0x%02X 0x%02X (%d)\n", |
| *(p - 1), (int)(0x40 + (pss->rng_rx & 0x3f)), |
| (int)((pss->position_rx - olen) + olen - len)); |
| lwsl_hexdump_notice(in, olen); |
| return -1; |
| } |
| } |
| if (pss->position_rx == MESSAGE_SIZE) |
| pss->position_rx = 0; |
| } |
| 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 \ |
| } |
| |
| #if !defined (LWS_PLUGIN_STATIC) |
| |
| /* boilerplate needed if we are built as a dynamic plugin */ |
| |
| static const struct lws_protocols protocols[] = { |
| LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK |
| }; |
| |
| LWS_EXTERN LWS_VISIBLE int |
| init_protocol_minimal_pmd_bulk(struct lws_context *context, |
| struct lws_plugin_capability *c) |
| { |
| if (c->api_magic != LWS_PLUGIN_API_MAGIC) { |
| lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, |
| c->api_magic); |
| return 1; |
| } |
| |
| c->protocols = protocols; |
| c->count_protocols = LWS_ARRAY_SIZE(protocols); |
| c->extensions = NULL; |
| c->count_extensions = 0; |
| |
| return 0; |
| } |
| |
| LWS_EXTERN LWS_VISIBLE int |
| destroy_protocol_minimal_pmd_bulk(struct lws_context *context) |
| { |
| return 0; |
| } |
| #endif |