diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 2e7b3fa0..72342c16 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1276,7 +1276,7 @@ LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info) #define LWS_LIBUV_ENABLED(context) (0) #if LWS_POSIX && !defined(LWS_WITH_ESP32) #define lws_feature_status_libuv(_a) \ - lwsl_notice("libuv support not compiled in\n") + lwsl_info("libuv support not compiled in\n") #else #define lws_feature_status_libuv(_a) #endif @@ -1307,7 +1307,7 @@ LWS_EXTERN void lws_feature_status_libevent(struct lws_context_creation_info *in #define LWS_LIBEVENT_ENABLED(context) (0) #if LWS_POSIX && !defined(LWS_WITH_ESP32) #define lws_feature_status_libevent(_a) \ - lwsl_notice("libevent support not compiled in\n") + lwsl_info("libevent support not compiled in\n") #else #define lws_feature_status_libevent(_a) #endif diff --git a/lib/service.c b/lib/service.c index 08c45819..dfead549 100644 --- a/lib/service.c +++ b/lib/service.c @@ -1491,7 +1491,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, lws_handle_POLLOUT_event(wsi, pollfd)) { if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY) wsi->state = LWSS_FLUSHING_SEND_BEFORE_CLOSE; - lwsl_notice("lws_service_fd: closing\n"); + // lwsl_notice("lws_service_fd: closing\n"); /* the write failed... it's had it */ wsi->socket_is_permanently_unusable = 1; goto close_and_handled; diff --git a/minimal-examples/minimal-http-client/CMakeLists.txt b/minimal-examples/minimal-http-client/CMakeLists.txt new file mode 100644 index 00000000..6c1a206c --- /dev/null +++ b/minimal-examples/minimal-http-client/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.8) + +project(lws-minimal-http-client C) + +set(SAMP lws-minimal-http-client) +set(SRCS minimal-http-client.c) + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets) + diff --git a/minimal-examples/minimal-http-client/README.md b/minimal-examples/minimal-http-client/README.md new file mode 100644 index 00000000..9d279557 --- /dev/null +++ b/minimal-examples/minimal-http-client/README.md @@ -0,0 +1,52 @@ +# lws minimal http client + +## build + +``` + $ cmake . && make +``` + +## usage + +The application goes to https://warmcat.com and receives the page data. + +``` + $ ./lws-minimal-http-client +[2018/03/04 14:43:20:8562] USER: LWS minimal http client +[2018/03/04 14:43:20:8571] NOTICE: Creating Vhost 'default' port -1, 1 protocols, IPv6 on +[2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default +[2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com +[2018/03/04 14:43:21:1496] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com +[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: incoming content length 26520 +[2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up +[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 +[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0174] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 +[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:0179] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 +[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3010] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 +[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3015] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 +[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3020] USER: RECEIVE_CLIENT_HTTP_READ: read 1015 +[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 +[2018/03/04 14:43:22:3022] USER: RECEIVE_CLIENT_HTTP_READ: read 974 +[2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1 +[2018/03/04 14:43:23:3042] USER: Completed +``` + + diff --git a/minimal-examples/minimal-http-client/minimal-http-client.c b/minimal-examples/minimal-http-client/minimal-http-client.c new file mode 100644 index 00000000..620fc0a1 --- /dev/null +++ b/minimal-examples/minimal-http-client/minimal-http-client.c @@ -0,0 +1,123 @@ +/* + * lws-minimal-http-client + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the a minimal http client using lws. + * + * It visits https://warmcat.com/ and receives the html page there. You + * can dump the page data by changing the #if 0 below. + */ + +#include +#include + +static struct lws *client_wsi; + +static int +callback_http(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + const char *p = in; + + switch (reason) { + + /* because we are protocols[0] ... */ + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", + in ? (char *)in : "(null)"); + client_wsi = NULL; + break; + + /* chunks of chunked content, with header removed */ + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: + lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); +#if 0 /* enable to dump the html */ + while (len--) + if (*p < 0x7f) + putchar(*p++); + else + putchar('.'); +#endif + return 0; /* don't passthru */ + + /* uninterpreted http content */ + case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: + { + char buffer[1024 + LWS_PRE]; + char *px = buffer + LWS_PRE; + int lenx = sizeof(buffer) - LWS_PRE; + + if (lws_http_client_read(wsi, &px, &lenx) < 0) + return -1; + } + return 0; /* don't passthru */ + + case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: + client_wsi = NULL; + break; + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static const struct lws_protocols protocols[] = { + { + "http", + callback_http, + 0, + 0, + }, + { NULL, NULL, 0, 0 } +}; + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_client_connect_info i; + struct lws_context *context; + int n = 0; + + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_USER, NULL); + lwsl_user("LWS minimal http client\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ + info.protocols = protocols; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ + i.context = context; + + i.port = 443; + i.address = "warmcat.com"; + i.path = "/"; + i.host = i.address; + i.origin = i.address; + i.ssl_connection = 1; + i.method = "GET"; + + i.protocol = protocols[0].name; + i.pwsi = &client_wsi; + lws_client_connect_via_info(&i); + + while (n >= 0 && client_wsi) + n = lws_service(context, 1000); + + lws_context_destroy(context); + lwsl_user("Completed\n"); + + return 0; +} diff --git a/minimal-examples/minimal-http-server/CMakeLists.txt b/minimal-examples/minimal-http-server/CMakeLists.txt new file mode 100644 index 00000000..38d6e751 --- /dev/null +++ b/minimal-examples/minimal-http-server/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.8) + +project(lws-minimal-http-server C) + +set(SAMP lws-minimal-http-server) +set(SRCS minimal-http-server.c) + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets) + diff --git a/minimal-examples/minimal-http-server/README.md b/minimal-examples/minimal-http-server/README.md new file mode 100644 index 00000000..cc8794b8 --- /dev/null +++ b/minimal-examples/minimal-http-server/README.md @@ -0,0 +1,18 @@ +# lws minimal http server + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-http-server +[2018/03/04 09:30:02:7986] USER: LWS minimal http 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 + diff --git a/minimal-examples/minimal-http-server/favicon.ico b/minimal-examples/minimal-http-server/favicon.ico new file mode 100644 index 00000000..c0cc2e3d Binary files /dev/null and b/minimal-examples/minimal-http-server/favicon.ico differ diff --git a/minimal-examples/minimal-http-server/index.html b/minimal-examples/minimal-http-server/index.html new file mode 100644 index 00000000..cd3e8bc7 --- /dev/null +++ b/minimal-examples/minimal-http-server/index.html @@ -0,0 +1,9 @@ + + + +
+ + Hello from the minimal http server example. + + + diff --git a/minimal-examples/minimal-http-server/libwebsockets.org-logo.png b/minimal-examples/minimal-http-server/libwebsockets.org-logo.png new file mode 100644 index 00000000..2060a10c Binary files /dev/null and b/minimal-examples/minimal-http-server/libwebsockets.org-logo.png differ diff --git a/minimal-examples/minimal-http-server/minimal-http-server.c b/minimal-examples/minimal-http-server/minimal-http-server.c new file mode 100644 index 00000000..ef8f7531 --- /dev/null +++ b/minimal-examples/minimal-http-server/minimal-http-server.c @@ -0,0 +1,63 @@ +/* + * lws-minimal-http-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 + +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, +}; + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + 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); + lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >=0) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/minimal-ws-server-ring/CMakeLists.txt b/minimal-examples/minimal-ws-server-ring/CMakeLists.txt new file mode 100644 index 00000000..a8864516 --- /dev/null +++ b/minimal-examples/minimal-ws-server-ring/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.8) + +project(lws-minimal-ws-server C) + +set(SAMP lws-minimal-ws-server) +set(SRCS minimal-ws-server.c) + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets) diff --git a/minimal-examples/minimal-ws-server-ring/README.md b/minimal-examples/minimal-ws-server-ring/README.md new file mode 100644 index 00000000..fb038851 --- /dev/null +++ b/minimal-examples/minimal-ws-server-ring/README.md @@ -0,0 +1,21 @@ +# lws minimal ws server (lws_ring) + +## build + +``` + $ cmake . && make +``` + +## usage + +``` + $ ./lws-minimal-ws-server +[2018/03/04 09:30:02:7986] USER: LWS minimal ws server (lws_ring) | 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. + +A ringbuffer holds up to 8 lines of text. diff --git a/minimal-examples/minimal-ws-server-ring/favicon.ico b/minimal-examples/minimal-ws-server-ring/favicon.ico new file mode 100644 index 00000000..c0cc2e3d Binary files /dev/null and b/minimal-examples/minimal-ws-server-ring/favicon.ico differ diff --git a/minimal-examples/minimal-ws-server-ring/index.html b/minimal-examples/minimal-ws-server-ring/index.html new file mode 100644 index 00000000..fed76a1b --- /dev/null +++ b/minimal-examples/minimal-ws-server-ring/index.html @@ -0,0 +1,84 @@ + + + + +
+ + LWS chat minimal ws server example.
+ Chat is sent to all browsers open on this page. +
+
+
+ + + + + + + + + + diff --git a/minimal-examples/minimal-ws-server-ring/libwebsockets.org-logo.png b/minimal-examples/minimal-ws-server-ring/libwebsockets.org-logo.png new file mode 100644 index 00000000..2060a10c Binary files /dev/null and b/minimal-examples/minimal-ws-server-ring/libwebsockets.org-logo.png differ diff --git a/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c b/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c new file mode 100644 index 00000000..77789bba --- /dev/null +++ b/minimal-examples/minimal-ws-server-ring/minimal-ws-server.c @@ -0,0 +1,73 @@ +/* + * 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 + +#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 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, +}; + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + 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); + lwsl_user("LWS minimal ws server (lws_ring) | visit http://localhost:7681\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >=0) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/minimal-ws-server-ring/protocol_lws_minimal.c b/minimal-examples/minimal-ws-server-ring/protocol_lws_minimal.c new file mode 100644 index 00000000..d25151cf --- /dev/null +++ b/minimal-examples/minimal-ws-server-ring/protocol_lws_minimal.c @@ -0,0 +1,235 @@ +/* + * 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 uses an lws_ring ringbuffer to cache up to 8 messages at a time, + * so it's not so easy to lose messages. + */ + +#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; + uint32_t tail; +}; + +/* 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 lws_ring *ring; /* ringbuffer holding unsent messages */ +}; + +/* 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)); + const struct msg *pmsg; + struct msg amsg; + 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); + + vhd->ring = lws_ring_create(sizeof(struct msg), 8, + __minimal_destroy_message); + break; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + lws_ring_destroy(vhd->ring); + 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->tail = lws_ring_get_oldest_tail(vhd->ring); + pss->wsi = 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 **, + 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: + pmsg = lws_ring_get_element(vhd->ring, &pss->tail); + if (!pmsg) + break; + + /* notice we allowed for LWS_PRE in the payload already */ + m = lws_write(wsi, pmsg->payload + LWS_PRE, pmsg->len, + LWS_WRITE_TEXT); + if (m < pmsg->len) { + lwsl_err("ERROR %d writing to di socket\n", n); + return -1; + } + + n = lws_ring_get_oldest_tail(vhd->ring) == pss->tail; + lws_ring_consume(vhd->ring, &pss->tail, NULL, 1); + + if (n) { /* we may have been the oldest tail */ + n = 0; + oldest = pss->tail; + lws_start_foreach_llp( + struct per_session_data__minimal **, + ppss, vhd->pss_list) { + m = lws_ring_get_count_waiting_elements( + vhd->ring, &(*ppss)->tail); + if (m > n) { + n = m; + oldest = (*ppss)->tail; + } + } lws_end_foreach_llp(ppss, pss_list); + + /* this will delete any entries behind the new oldest */ + lws_ring_update_oldest_tail(vhd->ring, oldest); + } + + /* more to do? */ + if (lws_ring_get_element(vhd->ring, &pss->tail)) + /* come back as soon as we can write more */ + lws_callback_on_writable((*ppss)->wsi); + break; + + case LWS_CALLBACK_RECEIVE: + n = (int)lws_ring_get_count_free_elements(vhd->ring); + if (!n) { + lwsl_user("dropping!\n"); + break; + } + + amsg.len = len; + /* notice we over-allocate by LWS_PRE */ + amsg.payload = malloc(LWS_PRE + len); + if (!amsg.payload) { + lwsl_user("OOM: dropping\n"); + break; + } + + memcpy((char *)amsg.payload + LWS_PRE, in, len); + if (!lws_ring_insert(vhd->ring, &amsg, 1)) { + __minimal_destroy_message(&amsg); + lwsl_user("dropping!\n"); + break; + } + + /* + * 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; + + case LWS_CALLBACK_TIMER: + lwsl_notice("%s: LWS_CALLBACK_TIMER\n", __func__); + lws_set_timer(wsi, 3); + 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/CMakeLists.txt b/minimal-examples/minimal-ws-server/CMakeLists.txt new file mode 100644 index 00000000..a8864516 --- /dev/null +++ b/minimal-examples/minimal-ws-server/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.8) + +project(lws-minimal-ws-server C) + +set(SAMP lws-minimal-ws-server) +set(SRCS minimal-ws-server.c) + +add_executable(${SAMP} ${SRCS}) +target_link_libraries(${SAMP} -lwebsockets) diff --git a/minimal-examples/minimal-ws-server/README.md b/minimal-examples/minimal-ws-server/README.md new file mode 100644 index 00000000..ad524e84 --- /dev/null +++ b/minimal-examples/minimal-ws-server/README.md @@ -0,0 +1,21 @@ +# lws minimal ws server + +## 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. diff --git a/minimal-examples/minimal-ws-server/favicon.ico b/minimal-examples/minimal-ws-server/favicon.ico new file mode 100644 index 00000000..c0cc2e3d Binary files /dev/null and b/minimal-examples/minimal-ws-server/favicon.ico differ diff --git a/minimal-examples/minimal-ws-server/index.html b/minimal-examples/minimal-ws-server/index.html new file mode 100644 index 00000000..5f42336b --- /dev/null +++ b/minimal-examples/minimal-ws-server/index.html @@ -0,0 +1,83 @@ + + + + +
+ + LWS chat minimal ws server example.
+ Chat is sent to all browsers open on this page. +
+
+
+ + + + + + + + + diff --git a/minimal-examples/minimal-ws-server/libwebsockets.org-logo.png b/minimal-examples/minimal-ws-server/libwebsockets.org-logo.png new file mode 100644 index 00000000..2060a10c Binary files /dev/null and b/minimal-examples/minimal-ws-server/libwebsockets.org-logo.png differ diff --git a/minimal-examples/minimal-ws-server/minimal-ws-server.c b/minimal-examples/minimal-ws-server/minimal-ws-server.c new file mode 100644 index 00000000..0f7ef0f9 --- /dev/null +++ b/minimal-examples/minimal-ws-server/minimal-ws-server.c @@ -0,0 +1,73 @@ +/* + * 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 + +#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 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, +}; + +int main(int argc, char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n = 0; + + 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); + lwsl_user("LWS minimal ws server | visit http://localhost:7681\n"); + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >=0) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/minimal-ws-server/protocol_lws_minimal.c b/minimal-examples/minimal-ws-server/protocol_lws_minimal.c new file mode 100644 index 00000000..87c2df2b --- /dev/null +++ b/minimal-examples/minimal-ws-server/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