diff --git a/lib/ext/extension-permessage-deflate.c b/lib/ext/extension-permessage-deflate.c index 80a98f66..e0c3773b 100644 --- a/lib/ext/extension-permessage-deflate.c +++ b/lib/ext/extension-permessage-deflate.c @@ -1,7 +1,7 @@ /* * ./lib/extension-permessage-deflate.c * - * Copyright (C) 2016 Andy Green + * Copyright (C) 2016 - 2018 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -83,7 +83,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context, if (!oa->option_name) break; for (n = 0; n < (int)ARRAY_SIZE(lws_ext_pm_deflate_options); n++) - if (!strcmp(lws_ext_pm_deflate_options[n].name, oa->option_name)) + if (!strcmp(lws_ext_pm_deflate_options[n].name, + oa->option_name)) break; if (n == (int)ARRAY_SIZE(lws_ext_pm_deflate_options)) @@ -123,8 +124,9 @@ lws_extension_callback_pm_deflate(struct lws_context *context, n = (int)wsi->protocol->rx_buffer_size; if (n < 128) { - lwsl_info(" permessage-deflate requires the protocol (%s) to have an RX buffer >= 128\n", - wsi->protocol->name); + lwsl_info(" permessage-deflate requires the protocol " + "(%s) to have an RX buffer >= 128\n", + wsi->protocol->name); return -1; } @@ -186,14 +188,16 @@ lws_extension_callback_pm_deflate(struct lws_context *context, printf("\n"); #endif if (!priv->rx_init) - if (inflateInit2(&priv->rx, -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) { + if (inflateInit2(&priv->rx, + -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) { lwsl_err("%s: iniflateInit failed\n", __func__); return -1; } priv->rx_init = 1; if (!priv->buf_rx_inflated) priv->buf_rx_inflated = lws_malloc(LWS_PRE + 7 + 5 + - (1 << priv->args[PMD_RX_BUF_PWR2]), "pmd rx inflate buf"); + (1 << priv->args[PMD_RX_BUF_PWR2]), + "pmd rx inflate buf"); if (!priv->buf_rx_inflated) { lwsl_err("%s: OOM\n", __func__); return -1; @@ -205,7 +209,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context, * rx buffer by the caller, so this assumption is safe while * we block new rx while draining the existing rx */ - if (!priv->rx.avail_in && eff_buf->token && eff_buf->token_len) { + if (!priv->rx.avail_in && eff_buf->token && + eff_buf->token_len) { priv->rx.next_in = (unsigned char *)eff_buf->token; priv->rx.avail_in = eff_buf->token_len; } @@ -266,8 +271,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context, priv->rx.next_in = trail; priv->rx.avail_in = sizeof(trail); n = inflate(&priv->rx, Z_SYNC_FLUSH); - lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", n, - priv->rx.avail_in, priv->rx.avail_out); + lwsl_ext("RX trailer inf returned %d, avi %d, avo %d\n", + n, priv->rx.avail_in, priv->rx.avail_out); switch (n) { case Z_NEED_DICT: case Z_STREAM_ERROR: @@ -302,7 +307,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context, priv->rx_held_valid = 1; } - eff_buf->token_len = lws_ptr_diff(priv->rx.next_out, eff_buf->token); + eff_buf->token_len = lws_ptr_diff(priv->rx.next_out, + eff_buf->token); priv->count_rx_between_fin += eff_buf->token_len; lwsl_ext(" %s: RX leaving with new effbuff len %d, " @@ -343,7 +349,8 @@ lws_extension_callback_pm_deflate(struct lws_context *context, priv->tx_init = 1; if (!priv->buf_tx_deflated) priv->buf_tx_deflated = lws_malloc(LWS_PRE + 7 + 5 + - (1 << priv->args[PMD_TX_BUF_PWR2]), "pmd tx deflate buf"); + (1 << priv->args[PMD_TX_BUF_PWR2]), + "pmd tx deflate buf"); if (!priv->buf_tx_deflated) { lwsl_err("%s: OOM\n", __func__); return -1; diff --git a/minimal-examples/minimal-http-client/minimal-http-client.c b/minimal-examples/minimal-http-client/minimal-http-client.c index 620fc0a1..07fd6461 100644 --- a/minimal-examples/minimal-http-client/minimal-http-client.c +++ b/minimal-examples/minimal-http-client/minimal-http-client.c @@ -84,7 +84,8 @@ int main(int argc, char **argv) struct lws_context *context; int n = 0; - lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER, NULL); + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); lwsl_user("LWS minimal http client\n"); memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ diff --git a/minimal-examples/minimal-http-server/minimal-http-server.c b/minimal-examples/minimal-http-server/minimal-http-server.c index ef8f7531..0e2e29e7 100644 --- a/minimal-examples/minimal-http-server/minimal-http-server.c +++ b/minimal-examples/minimal-http-server/minimal-http-server.c @@ -14,6 +14,9 @@ #include #include +#include + +static int interrupted; static const struct lws_http_mount mount = { /* .mount_next */ NULL, /* linked-list "next" */ @@ -35,17 +38,26 @@ static const struct lws_http_mount mount = { /* .basic_auth_login_file */ NULL, }; +void sigint_handler(int sig) +{ + interrupted = 1; +} + int main(int argc, char **argv) { struct lws_context_creation_info info; struct lws_context *context; int n = 0; + signal(SIGINT, sigint_handler); + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ info.port = 7681; info.mounts = &mount; - lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER, NULL); + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); + lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); context = lws_create_context(&info); @@ -54,7 +66,7 @@ int main(int argc, char **argv) return 1; } - while (n >=0) + while (n >= 0 && !interrupted) n = lws_service(context, 1000); lws_context_destroy(context); diff --git a/minimal-examples/minimal-ws-server-pmd-bulk/CMakeLists.txt b/minimal-examples/minimal-ws-server-pmd-bulk/CMakeLists.txt new file mode 100644 index 00000000..1f9b1cc9 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd-bulk/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.8.9) +include(CheckFunctionExists) + +set(SAMP lws-minimal-ws-server-pmd-bulk) +set(SRCS minimal-ws-server-pmd-bulk.c) + +set(CMAKE_REQUIRED_LIBRARIES websockets) + +CHECK_FUNCTION_EXISTS(lws_extension_callback_pm_deflate HAVE_PMD) +if (HAVE_PMD) +else() + message(FATAL_ERROR "LWS need to have been built for extensions") +endif() + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets) diff --git a/minimal-examples/minimal-ws-server-pmd-bulk/README.md b/minimal-examples/minimal-ws-server-pmd-bulk/README.md new file mode 100644 index 00000000..274dbf98 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd-bulk/README.md @@ -0,0 +1,21 @@ +# lws minimal ws server + permessage-deflate for bulk traffic + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-ws-server-pmd-bulk +[2018/03/04 09:30:02:7986] USER: LWS minimal ws server | visit http://localhost:7681 +[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on +``` + +Visit http://localhost:7681 in your browser + +One or another kind of bulk ws transfer is made to the browser. + +The ws connection is made via permessage-deflate extension. diff --git a/minimal-examples/minimal-ws-server-pmd-bulk/index.html b/minimal-examples/minimal-ws-server-pmd-bulk/index.html new file mode 100644 index 00000000..07cf2b25 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd-bulk/index.html @@ -0,0 +1,87 @@ + + + + +
+ + LWS bulk transfer example.
+ A large ws message is sent to all browsers open on this page.
+
+ Ws closed
+
+
+ + + + + + + + + diff --git a/minimal-examples/minimal-ws-server-pmd-bulk/libwebsockets.org-logo.png b/minimal-examples/minimal-ws-server-pmd-bulk/libwebsockets.org-logo.png new file mode 100644 index 00000000..2060a10c Binary files /dev/null and b/minimal-examples/minimal-ws-server-pmd-bulk/libwebsockets.org-logo.png differ diff --git a/minimal-examples/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c b/minimal-examples/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c new file mode 100644 index 00000000..15867752 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd-bulk/minimal-ws-server-pmd-bulk.c @@ -0,0 +1,98 @@ +/* + * lws-minimal-ws-server + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the most minimal http server you can make with lws. + * + * To keep it simple, it serves stuff in the directory it was started in. + * You can change that by changing mount.origin + */ + +#include +#include +#include + +#define LWS_PLUGIN_STATIC +#include "protocol_lws_minimal_pmd_bulk.c" + +static struct lws_protocols protocols[] = { + { "http", lws_callback_http_dummy, 0, 0 }, + LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static int interrupted; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ ".", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +static const struct lws_extension extensions[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate" + "; client_no_context_takeover" + "; client_max_window_bits" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + signal(SIGINT, sigint_handler); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.mounts = &mount; + info.protocols = protocols; + info.extensions = extensions; + info.pt_serv_buf_size = 32 * 1024; + + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); + + lwsl_user("LWS minimal ws server + permessage-deflate | visit http://localhost:7681\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c b/minimal-examples/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c new file mode 100644 index 00000000..1ceafe8c --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c @@ -0,0 +1,224 @@ +/* + * ws protocol handler plugin for "lws-minimal-pmd-bulk" + * + * Copyright (C) 2010-2018 Andy Green + * + * 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 +#endif + +#include + +/* + * 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 { + struct per_session_data__minimal_pmd_bulk *pss_list; + struct lws *wsi; + int position; /* byte position we got up to sending the message */ + uint64_t rng; +}; + +/* one of these is created for each vhost our protocol is used with */ + +struct per_vhost_data__minimal_pmd_bulk { + struct lws_context *context; + struct lws_vhost *vhost; + const struct lws_protocols *protocol; + + /* linked-list of live pss */ + struct per_session_data__minimal_pmd_bulk *pss_list; +}; + +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 **ppss, *pss = + (struct per_session_data__minimal_pmd_bulk *)user; + struct per_vhost_data__minimal_pmd_bulk *vhd = + (struct per_vhost_data__minimal_pmd_bulk *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + uint8_t buf[LWS_PRE + MESSAGE_CHUNK_SIZE], *p; + uint32_t oldest; + int n, m, s, msg_flag = LWS_WRITE_CONTINUATION; + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct per_vhost_data__minimal_pmd_bulk)); + vhd->context = lws_get_context(wsi); + vhd->protocol = lws_get_protocol(wsi); + vhd->vhost = lws_get_vhost(wsi); + break; + + case LWS_CALLBACK_ESTABLISHED: + /* add ourselves to the list of live pss held in the vhd */ + pss->pss_list = vhd->pss_list; + vhd->pss_list = pss; + pss->wsi = wsi; + pss->position = 0; + pss->rng = 4; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLOSED: + /* remove our closing pss from the list of live pss */ + lws_start_foreach_llp(struct per_session_data__minimal_pmd_bulk **, + ppss, vhd->pss_list) { + if (*ppss == pss) { + *ppss = pss->pss_list; + break; + } + } lws_end_foreach_llp(ppss, pss_list); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + + if (pss->position == MESSAGE_SIZE) + break; + + if (pss->position == 0) + msg_flag = LWS_WRITE_TEXT; + + /* fill up one chunk's worth of message content */ + + p = &buf[LWS_PRE]; + n = MESSAGE_CHUNK_SIZE; + if (n > MESSAGE_SIZE - pss->position) + n = MESSAGE_SIZE - pss->position; + /* + * select between producing compressible repeated text, + * or uncompressible PRNG output + */ +#if 0 + while (n) { + m = pss->position % REPEAT_STRING_LEN; + s = REPEAT_STRING_LEN - m; + if (s > n) + s = n; + memcpy(p, &redundant_string[m], s); + pss->position += s; + p += s; + n -= s; + } +#else + pss->position += n; + while (n--) { + pss->rng ^= pss->rng << 21; + pss->rng ^= pss->rng >> 35; + pss->rng ^= pss->rng << 4; + *p++ = 0x40 + ((pss->rng >> (n & 15)) & 0x3f); + } +#endif + if (pss->position != MESSAGE_SIZE) /* if not the end, no FIN */ + msg_flag |= LWS_WRITE_NO_FIN; + + n = lws_ptr_diff(p, &buf[LWS_PRE]); + m = lws_write(wsi, &buf[LWS_PRE], n, msg_flag); + lwsl_notice("write done\n"); + if (m < n) { + lwsl_err("ERROR %d writing to di socket\n", n); + return -1; + } + if (pss->position != MESSAGE_SIZE) /* if more to do... */ + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_RECEIVE: + 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 = 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 diff --git a/minimal-examples/minimal-ws-server-pmd/CMakeLists.txt b/minimal-examples/minimal-ws-server-pmd/CMakeLists.txt new file mode 100644 index 00000000..4d07b7b1 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.8.9) +include(CheckFunctionExists) + +set(SAMP lws-minimal-ws-server-pmd) +set(SRCS minimal-ws-server-pmd.c) + +set(CMAKE_REQUIRED_LIBRARIES websockets) + +CHECK_FUNCTION_EXISTS(lws_extension_callback_pm_deflate HAVE_PMD) +if (HAVE_PMD) +else() + message(FATAL_ERROR "LWS need to have been built for extensions") +endif() + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets) diff --git a/minimal-examples/minimal-ws-server-pmd/README.md b/minimal-examples/minimal-ws-server-pmd/README.md new file mode 100644 index 00000000..468f74f9 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd/README.md @@ -0,0 +1,23 @@ +# lws minimal ws server + permessage-deflate + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-ws-server +[2018/03/04 09:30:02:7986] USER: LWS minimal ws server | visit http://localhost:7681 +[2018/03/04 09:30:02:7986] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 on +``` + +Visit http://localhost:7681 on multiple browser windows + +Text you type in any browser window is sent to all of them. + +For simplicity of this example, only one line of text is cached at the server. + +The ws connection is made via permessage-deflate extension. diff --git a/minimal-examples/minimal-ws-server-pmd/favicon.ico b/minimal-examples/minimal-ws-server-pmd/favicon.ico new file mode 100644 index 00000000..c0cc2e3d Binary files /dev/null and b/minimal-examples/minimal-ws-server-pmd/favicon.ico differ diff --git a/minimal-examples/minimal-ws-server-pmd/index.html b/minimal-examples/minimal-ws-server-pmd/index.html new file mode 100644 index 00000000..74750344 --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd/index.html @@ -0,0 +1,86 @@ + + + + +
+ + LWS chat minimal ws server example.
+ Chat is sent to all browsers open on this page.
+
+ Ws closed
+
+
+ + + + + + + + + diff --git a/minimal-examples/minimal-ws-server-pmd/libwebsockets.org-logo.png b/minimal-examples/minimal-ws-server-pmd/libwebsockets.org-logo.png new file mode 100644 index 00000000..2060a10c Binary files /dev/null and b/minimal-examples/minimal-ws-server-pmd/libwebsockets.org-logo.png differ diff --git a/minimal-examples/minimal-ws-server-pmd/minimal-ws-server-pmd.c b/minimal-examples/minimal-ws-server-pmd/minimal-ws-server-pmd.c new file mode 100644 index 00000000..b77e7d0a --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd/minimal-ws-server-pmd.c @@ -0,0 +1,97 @@ +/* + * lws-minimal-ws-server + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the most minimal http server you can make with lws. + * + * To keep it simple, it serves stuff in the directory it was started in. + * You can change that by changing mount.origin + */ + +#include +#include +#include + +#define LWS_PLUGIN_STATIC +#include "protocol_lws_minimal.c" + +static struct lws_protocols protocols[] = { + { "http", lws_callback_http_dummy, 0, 0 }, + LWS_PLUGIN_PROTOCOL_MINIMAL, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static int interrupted; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ ".", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +static const struct lws_extension extensions[] = { + { + "permessage-deflate", + lws_extension_callback_pm_deflate, + "permessage-deflate" + "; client_no_context_takeover" + "; client_max_window_bits" + }, + { NULL, NULL, NULL /* terminator */ } +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + signal(SIGINT, sigint_handler); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.mounts = &mount; + info.protocols = protocols; + info.extensions = extensions; + + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); + + lwsl_user("LWS minimal ws server + permessage-deflate | visit http://localhost:7681\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/minimal-ws-server-pmd/protocol_lws_minimal.c b/minimal-examples/minimal-ws-server-pmd/protocol_lws_minimal.c new file mode 100644 index 00000000..87c2df2b --- /dev/null +++ b/minimal-examples/minimal-ws-server-pmd/protocol_lws_minimal.c @@ -0,0 +1,194 @@ +/* + * ws protocol handler plugin for "lws-minimal" + * + * Copyright (C) 2010-2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This version holds a single message at a time, which may be lost if a new + * message comes. See the minimal-ws-server-ring sample for the same thing + * but using an lws_ring ringbuffer to hold up to 8 messages at a time. + */ + +#if !defined (LWS_PLUGIN_STATIC) +#define LWS_DLL +#define LWS_INTERNAL +#include +#endif + +#include + +/* one of these created for each message */ + +struct msg { + void *payload; /* is malloc'd */ + size_t len; +}; + +/* one of these is created for each client connecting to us */ + +struct per_session_data__minimal { + struct per_session_data__minimal *pss_list; + struct lws *wsi; + int last; /* the last message number we sent */ +}; + +/* one of these is created for each vhost our protocol is used with */ + +struct per_vhost_data__minimal { + struct lws_context *context; + struct lws_vhost *vhost; + const struct lws_protocols *protocol; + + struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ + + struct msg amsg; /* the one pending message... */ + int current; /* the current message number we are caching */ +}; + +/* destroys the message when everyone has had a copy of it */ + +static void +__minimal_destroy_message(void *_msg) +{ + struct msg *msg = _msg; + + free(msg->payload); + msg->payload = NULL; + msg->len = 0; +} + +static int +callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct per_session_data__minimal **ppss, *pss = + (struct per_session_data__minimal *)user; + struct per_vhost_data__minimal *vhd = + (struct per_vhost_data__minimal *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + uint32_t oldest; + int n, m; + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), + sizeof(struct per_vhost_data__minimal)); + vhd->context = lws_get_context(wsi); + vhd->protocol = lws_get_protocol(wsi); + vhd->vhost = lws_get_vhost(wsi); + break; + + case LWS_CALLBACK_ESTABLISHED: + /* add ourselves to the list of live pss held in the vhd */ + pss->pss_list = vhd->pss_list; + vhd->pss_list = pss; + pss->wsi = wsi; + pss->last = vhd->current; + break; + + case LWS_CALLBACK_CLOSED: + /* remove our closing pss from the list of live pss */ + lws_start_foreach_llp(struct per_session_data__minimal **, + ppss, vhd->pss_list) { + if (*ppss == pss) { + *ppss = pss->pss_list; + break; + } + } lws_end_foreach_llp(ppss, pss_list); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + if (!vhd->amsg.payload) + break; + + if (pss->last == vhd->current) + break; + + /* notice we allowed for LWS_PRE in the payload already */ + m = lws_write(wsi, vhd->amsg.payload + LWS_PRE, vhd->amsg.len, + LWS_WRITE_TEXT); + if (m < vhd->amsg.len) { + lwsl_err("ERROR %d writing to di socket\n", n); + return -1; + } + + pss->last = vhd->current; + break; + + case LWS_CALLBACK_RECEIVE: + if (vhd->amsg.payload) + __minimal_destroy_message(&vhd->amsg); + + vhd->amsg.len = len; + /* notice we over-allocate by LWS_PRE */ + vhd->amsg.payload = malloc(LWS_PRE + len); + if (!vhd->amsg.payload) { + lwsl_user("OOM: dropping\n"); + break; + } + + memcpy((char *)vhd->amsg.payload + LWS_PRE, in, len); + vhd->current++; + + /* + * let everybody know we want to write something on them + * as soon as they are ready + */ + lws_start_foreach_llp(struct per_session_data__minimal **, + ppss, vhd->pss_list) { + lws_callback_on_writable((*ppss)->wsi); + } lws_end_foreach_llp(ppss, pss_list); + break; + + default: + break; + } + + return 0; +} + +#define LWS_PLUGIN_PROTOCOL_MINIMAL \ + { \ + "lws-minimal", \ + callback_minimal, \ + sizeof(struct per_session_data__minimal), \ + 128, \ + 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 +}; + +LWS_EXTERN LWS_VISIBLE int +init_protocol_minimal(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 = ARRAY_SIZE(protocols); + c->extensions = NULL; + c->count_extensions = 0; + + return 0; +} + +LWS_EXTERN LWS_VISIBLE int +destroy_protocol_minimal(struct lws_context *context) +{ + return 0; +} +#endif diff --git a/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c b/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c index 77789bba..dcc805da 100644 --- a/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c +++ b/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c @@ -6,7 +6,8 @@ * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * - * This demonstrates the most minimal http server you can make with lws. + * This demonstrates the most minimal http server you can make with lws, + * with an added websocket chat server using a ringbuffer. * * To keep it simple, it serves stuff in the directory it was started in. * You can change that by changing mount.origin @@ -14,6 +15,7 @@ #include #include +#include #define LWS_PLUGIN_STATIC #include "protocol_lws_minimal.c" @@ -24,6 +26,8 @@ static struct lws_protocols protocols[] = { { NULL, NULL, 0, 0 } /* terminator */ }; +static int interrupted; + static const struct lws_http_mount mount = { /* .mount_next */ NULL, /* linked-list "next" */ /* .mountpoint */ "/", /* mountpoint URL */ @@ -44,18 +48,27 @@ static const struct lws_http_mount mount = { /* .basic_auth_login_file */ NULL, }; +void sigint_handler(int sig) +{ + interrupted = 1; +} + int main(int argc, char **argv) { struct lws_context_creation_info info; struct lws_context *context; int n = 0; + signal(SIGINT, sigint_handler); + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ info.port = 7681; info.mounts = &mount; info.protocols = protocols; - lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER, NULL); + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); + lwsl_user("LWS minimal ws server (lws_ring) | visit http://localhost:7681\n"); context = lws_create_context(&info); @@ -64,7 +77,7 @@ int main(int argc, char **argv) return 1; } - while (n >=0) + while (n >= 0 && !interrupted) n = lws_service(context, 1000); lws_context_destroy(context); diff --git a/minimal-examples/minimal-ws-server/minimal-ws-server.c b/minimal-examples/minimal-ws-server/minimal-ws-server.c index 0f7ef0f9..56245bab 100644 --- a/minimal-examples/minimal-ws-server/minimal-ws-server.c +++ b/minimal-examples/minimal-ws-server/minimal-ws-server.c @@ -6,7 +6,8 @@ * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * - * This demonstrates the most minimal http server you can make with lws. + * This demonstrates the most minimal http server you can make with lws, + * with an added websocket chat server. * * To keep it simple, it serves stuff in the directory it was started in. * You can change that by changing mount.origin @@ -14,6 +15,7 @@ #include #include +#include #define LWS_PLUGIN_STATIC #include "protocol_lws_minimal.c" @@ -24,6 +26,8 @@ static struct lws_protocols protocols[] = { { NULL, NULL, 0, 0 } /* terminator */ }; +static int interrupted; + static const struct lws_http_mount mount = { /* .mount_next */ NULL, /* linked-list "next" */ /* .mountpoint */ "/", /* mountpoint URL */ @@ -44,18 +48,27 @@ static const struct lws_http_mount mount = { /* .basic_auth_login_file */ NULL, }; +void sigint_handler(int sig) +{ + interrupted = 1; +} + int main(int argc, char **argv) { struct lws_context_creation_info info; struct lws_context *context; int n = 0; + signal(SIGINT, sigint_handler); + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ info.port = 7681; info.mounts = &mount; info.protocols = protocols; - lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER, NULL); + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER + /* | LLL_INFO */ /* | LLL_DEBUG */, NULL); + lwsl_user("LWS minimal ws server | visit http://localhost:7681\n"); context = lws_create_context(&info); @@ -64,7 +77,7 @@ int main(int argc, char **argv) return 1; } - while (n >=0) + while (n >= 0 && !interrupted) n = lws_service(context, 1000); lws_context_destroy(context);