diff --git a/CMakeLists.txt b/CMakeLists.txt index e9ad1d2d1..3ca63611b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(LWS_WITH_HTTP_BROTLI "Also offer brotli http stream compression (requires option(LWS_WITH_ACME "Enable support for ACME automatic cert acquisition + maintenance (letsencrypt etc)" OFF) option(LWS_WITH_HUBBUB "Enable libhubbub rewriting support" OFF) option(LWS_WITH_ALSA "Enable alsa audio example" OFF) +option(LWS_WITH_GTK "Enable gtk example" OFF) option(LWS_WITH_FTS "Full Text Search support" OFF) option(LWS_WITH_SYS_ASYNC_DNS "Nonblocking internal IPv4 + IPv6 DNS resolver" OFF) option(LWS_WITH_SYS_NTPCLIENT "Build in tiny ntpclient good for tls date validation and run via lws_system" OFF) @@ -69,6 +70,8 @@ option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of the OS-ins option(LWS_WITH_LIBEV "Compile with support for libev" OFF) option(LWS_WITH_LIBUV "Compile with support for libuv" OFF) option(LWS_WITH_LIBEVENT "Compile with support for libevent" OFF) +option(LWS_WITH_GLIB "Compile with support for glib event loop" OFF) + # # Static / Dynamic build options # @@ -204,6 +207,7 @@ if(LWS_WITH_DISTRO_RECOMMENDED) set(LWS_WITH_RANGES 1) set(LWS_WITH_ACME 1) set(LWS_WITH_SERVER_STATUS 1) + set(LWS_WITH_GLIB 1) set(LWS_WITH_LIBUV 1) set(LWS_WITH_LIBEV 1) # libev + libevent cannot coexist at build-time @@ -365,7 +369,7 @@ if (LWS_WITH_MBEDTLS) include_directories(lib/tls/mbedtls/wrapper/include) endif() -include_directories(include plugins lib/core lib/core-net lib/event-libs include/abstract lib/tls lib/roles lib/event-libs/libuv lib/event-libs/poll lib/event-libs/libevent lib/event-libs/libev lib/jose/jwe lib/jose/jws lib/jose lib/misc lib/roles/http lib/roles/http/compression lib/roles/h1 lib/roles/h2 lib/roles/ws lib/roles/cgi lib/roles/dbus lib/roles/raw-proxy lib/abstract lib/system/async-dns) +include_directories(include plugins lib/core lib/core-net lib/event-libs include/abstract lib/tls lib/roles lib/event-libs/libuv lib/event-libs/poll lib/event-libs/libevent lib/event-libs/glib lib/event-libs/libev lib/jose/jwe lib/jose/jws lib/jose lib/misc lib/roles/http lib/roles/http/compression lib/roles/h1 lib/roles/h2 lib/roles/ws lib/roles/cgi lib/roles/dbus lib/roles/raw-proxy lib/abstract lib/system/async-dns) if (LWS_PLAT_FREERTOS) include_directories(lib/plat/freertos lib/plat/freertos/esp32) @@ -544,6 +548,9 @@ set(LWS_SQLITE3_LIBRARIES CACHE PATH "Path to the sqlite3 library") set(LWS_SQLITE3_INCLUDE_DIRS CACHE PATH "Path to the sqlite3 include directory") set(LWS_LIBEVENT_INCLUDE_DIRS CACHE PATH "Path to the libevent include directory") set(LWS_LIBEVENT_LIBRARIES CACHE PATH "Path to the libevent library") +set(LWS_GLIB_INCLUDE_DIRS CACHE PATH "Path to the glib include directory") +set(LWS_GLIB_LIBRARIES CACHE PATH "Path to the glib library") + if (NOT LWS_WITH_SSL) @@ -654,6 +661,15 @@ if (LWS_WITH_LIBEVENT) endif() endif() +if (LWS_WITH_GLIB) + if ("${LWS_GLIB_LIBRARIES}" STREQUAL "" OR "${LWS_GLIB_INCLUDE_DIRS}" STREQUAL "") + else() + set(LIBGLIB_LIBRARIES ${LWS_GLIB_LIBRARIES}) + set(LIBGLIB_INCLUDE_DIRS ${LWS_GLIB_INCLUDE_DIRS}) + set(LIBGLIB_FOUND 1) + endif() +endif() + if (LWS_WITH_SQLITE3) if ("${LWS_SQLITE3_LIBRARIES}" STREQUAL "" OR "${LWS_SQLITE3_INCLUDE_DIRS}" STREQUAL "") else() @@ -1454,6 +1470,12 @@ if (LWS_WITH_LIBEVENT AND LWS_WITH_NETWORK) lib/event-libs/libevent/libevent.c) endif() +if (LWS_WITH_GLIB AND LWS_WITH_NETWORK) + list(APPEND SOURCES + lib/event-libs/glib/glib.c) +endif() + + if (LWS_WITH_LIBEV AND LWS_WITH_NETWORK) list(APPEND SOURCES lib/event-libs/libev/libev.c) @@ -1893,6 +1915,30 @@ if (LWS_WITH_LIBEVENT) list(APPEND LIB_LIST ${LIBEVENT_LIBRARIES}) endif(LWS_WITH_LIBEVENT) +if (LWS_WITH_GLIB) + include (FindPkgConfig) + if (NOT GLIB_FOUND) + find_path(GLIB_INCLUDE_DIRS NAMES glib-2.0/glib.h) + find_library(GLIB_LIBRARIES NAMES glib-2.0) + if(GLIB_INCLUDE_DIRS AND GLIB_LIBRARIES) + set(GLIB_FOUND 1) + endif() + if (GLIB_INCLUDE_DIRS) + set(GLIB_INCLUDE_DIRS "${GLIB_INCLUDE_DIRS}/glib-2.0") + endif() + endif() + PKG_SEARCH_MODULE(LWS_GLIB2 glib-2.0) + if (LWS_GLIB2_FOUND) + list(APPEND GLIB_INCLUDE_DIRS "${LWS_GLIB2_INCLUDE_DIRS}") + endif() + message("glib include dir: ${GLIB_INCLUDE_DIRS}") + message("glib libraries: ${GLIB_LIBRARIES}") + include_directories("${GLIB_INCLUDE_DIRS}") + list(APPEND LIB_LIST ${GLIB_LIBRARIES}) +endif(LWS_WITH_GLIB) + + + if (LWS_WITH_SQLITE3) if (NOT SQLITE3_FOUND) find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h) @@ -2462,7 +2508,7 @@ if (LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS AND LWS_WITH_TLS) target_link_libraries(protocol_lws_messageboard sqlite3 ) endif(WIN32) -endif(LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS) +endif(LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS AND LWS_WITH_TLS) endif(LWS_WITH_PLUGINS AND LWS_WITH_SHARED) @@ -2790,6 +2836,7 @@ message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}") message(" LWS_WITH_LIBEV = ${LWS_WITH_LIBEV}") message(" LWS_WITH_LIBUV = ${LWS_WITH_LIBUV}") message(" LWS_WITH_LIBEVENT = ${LWS_WITH_LIBEVENT}") +message(" LWS_WITH_GLIB = ${LWS_WITH_GLIB}") message(" LWS_IPV6 = ${LWS_IPV6}") message(" LWS_UNIX_SOCK = ${LWS_UNIX_SOCK}") message(" LWS_WITH_HTTP2 = ${LWS_WITH_HTTP2}") diff --git a/README.md b/README.md index 62c86c868..cddaa3bf3 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,20 @@ various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to News ---- +## libglib native event loop support + +glib's event loop joins libuv, libevent and libev support in lws for both the +`lws_context` creating and owning the loop object for its lifetime, and for +an already-existing "foreign loop" where the `lws_context` is created, attaches, +detaches, and is destroyed without affecting the loop. + +This allows direct, lock-free integration of lws functionality with, eg, a GTK app's +existing `GMainLoop` / glib `g_main_loop`. Just select `-DLWS_WITH_GLIB=1` at cmake +time to enable. The -eventlib minimal examples also support --glib option to +select using the glib loop at runtime. + +There's also a gtk example that is built if lws cmake has `-DLWS_WITH_GTK=1`. + ## `lws_system` helper for attaching code to a single event loop from another thread `lws_system` ops struct now has a member that enables other threads (in the diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 3ef6bff99..d284cd801 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -110,6 +110,8 @@ #cmakedefine LWS_WITH_FTS #cmakedefine LWS_WITH_GENCRYPTO #cmakedefine LWS_WITH_GENERIC_SESSIONS +#cmakedefine LWS_WITH_GLIB +#cmakedefine LWS_WITH_GTK #cmakedefine LWS_WITH_HTTP2 #cmakedefine LWS_WITH_HTTP_BROTLI #cmakedefine LWS_WITH_HTTP_PROXY diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index fa93a59df..a2b21318a 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -218,6 +218,9 @@ * poll mode. */ +#define LWS_SERVER_OPTION_GLIB (1ll << 33) + /**< (CTX) Use glib event loop */ + /****** add new things just above ---^ ******/ diff --git a/lib/core-net/close.c b/lib/core-net/close.c index 2dabc69c9..ac796b8ed 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -507,7 +507,7 @@ just_kill_connection: if (!wsi->socket_is_permanently_unusable && lws_socket_is_valid(wsi->desc.sockfd) && lwsi_state(wsi) != LRS_SHUTDOWN && - context->event_loop_ops->periodic_events_available) { + (context->event_loop_ops->flags & LELOF_ISPOLL)) { __lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN); lwsi_set_state(wsi, LRS_SHUTDOWN); __lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH, diff --git a/lib/core-net/pollfd.c b/lib/core-net/pollfd.c index c9728edca..cce381142 100644 --- a/lib/core-net/pollfd.c +++ b/lib/core-net/pollfd.c @@ -73,7 +73,8 @@ _lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa) #if !defined(LWS_WITH_LIBUV) && \ !defined(LWS_WITH_LIBEV) && \ - !defined(LWS_WITH_LIBEVENT) + !defined(LWS_WITH_LIBEVENT) && \ + !defined(LWS_WITH_GLIB) /* * This only applies when we use the default poll() event loop. * diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index fa9157208..beac533dd 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -429,6 +429,9 @@ struct lws_context_per_thread { #if defined(LWS_WITH_LIBEVENT) struct lws_pt_eventlibs_libevent event; #endif +#if defined(LWS_WITH_GLIB) + struct lws_pt_eventlibs_glib glib; +#endif #if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ defined(LWS_WITH_LIBEVENT) @@ -649,7 +652,7 @@ struct lws { /* lifetime members */ #if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ - defined(LWS_WITH_LIBEVENT) + defined(LWS_WITH_LIBEVENT) || defined(LWS_WITH_GLIB) struct lws_io_watcher w_read; #endif #if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) diff --git a/lib/core/context.c b/lib/core/context.c index c421cb5a6..10d2762a2 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -409,6 +409,13 @@ lws_create_context(const struct lws_context_creation_info *info) goto fail_event_libs; #endif + if (lws_check_opt(context->options, LWS_SERVER_OPTION_GLIB)) +#if defined(LWS_WITH_GLIB) + context->event_loop_ops = &event_loop_ops_glib; +#else + goto fail_event_libs; +#endif + if (!context->event_loop_ops) goto fail_event_libs; diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index fdd9dba89..e9aacd06c 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -207,6 +207,9 @@ struct lws_io_watcher { #endif #ifdef LWS_WITH_LIBEVENT struct lws_io_watcher_libevent event; +#endif +#ifdef LWS_WITH_GLIB + struct lws_io_watcher_glib glib; #endif struct lws_context *context; @@ -222,6 +225,9 @@ struct lws_signal_watcher { #endif #ifdef LWS_WITH_LIBEVENT struct lws_signal_watcher_libevent event; +#endif +#ifdef LWS_WITH_LIBEVENT + struct lws_signal_watcher_glib glib; #endif struct lws_context *context; }; @@ -339,6 +345,9 @@ struct lws_context { #if defined(LWS_WITH_LIBEVENT) struct lws_context_eventlibs_libevent event; #endif +#if defined(LWS_WITH_GLIB) + struct lws_context_eventlibs_glib glib; +#endif #if defined(LWS_WITH_TLS) struct lws_context_tls tls; diff --git a/lib/event-libs/glib/glib.c b/lib/event-libs/glib/glib.c new file mode 100644 index 000000000..922929687 --- /dev/null +++ b/lib/event-libs/glib/glib.c @@ -0,0 +1,464 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2020 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "private-lib-core.h" + +#include + +#define wsi_to_subclass(_w) ((_w)->w_read.glib.source) +#define wsi_to_gsource(_w) ((GSource *)wsi_to_subclass(_w)) +#define pt_to_loop(_pt) ((_pt)->glib.loop) +#define pt_to_g_main_context(_pt) g_main_loop_get_context(pt_to_loop(_pt)) + +static gboolean +lws_glib_idle_timer_cb(void *p); + +static gboolean +lws_glib_hrtimer_cb(void *p); + +static gboolean +lws_glib_check(GSource *src) +{ + struct lws_io_watcher_glib_subclass *sub = + (struct lws_io_watcher_glib_subclass *)src; + + return !!g_source_query_unix_fd(src, sub->tag); +} + +/* + * These helpers attach only to the main_context that belongs to the pt's glib + * mainloop. The simpler g_timeout_add() and g_idle_add() are forbidden + * because they implicitly choose the default main context to attach to + * instead of specifically the loop bound to the pt. + * + * https://developer.gnome.org/programming-guidelines/unstable/main-contexts.html.en#what-is-gmaincontext + */ + +static int +lws_glib_set_idle(struct lws_context_per_thread *pt) +{ + GSource *gis; + + if (pt->glib.idle_tag) + return 0; + + gis = g_idle_source_new(); + if (!gis) + return 1; + + g_source_set_callback(gis, lws_glib_idle_timer_cb, pt, NULL); + pt->glib.idle_tag = g_source_attach(gis, pt_to_g_main_context(pt)); + + return 0; +} + +static int +lws_glib_set_timeout(struct lws_context_per_thread *pt, unsigned int ms) +{ + GSource *gts; + + gts = g_timeout_source_new(ms); + if (!gts) + return 1; + + g_source_set_callback(gts, lws_glib_hrtimer_cb, pt, NULL); + pt->glib.hrtimer_tag = g_source_attach(gts, pt_to_g_main_context(pt)); + + return 0; +} + +static gboolean +lws_glib_dispatch(GSource *src, GSourceFunc x, gpointer userData) +{ + struct lws_io_watcher_glib_subclass *sub = + (struct lws_io_watcher_glib_subclass *)src; + struct lws_context_per_thread *pt; + struct lws_pollfd eventfd; + GIOCondition cond; + + cond = g_source_query_unix_fd(src, sub->tag); + eventfd.revents = cond; + + /* translate from glib event namespace to platform */ + + if (cond & G_IO_IN) + eventfd.revents |= LWS_POLLIN; + if (cond & G_IO_OUT) + eventfd.revents |= LWS_POLLOUT; + if (cond & G_IO_ERR) + eventfd.revents |= LWS_POLLHUP; + if (cond & G_IO_HUP) + eventfd.revents |= LWS_POLLHUP; + + eventfd.events = eventfd.revents; + eventfd.fd = sub->wsi->desc.sockfd; + + lwsl_debug("%s: wsi %p: fd %d, events %d\n", __func__, sub->wsi, + eventfd.fd, eventfd.revents); + + pt = &sub->wsi->context->pt[(int)sub->wsi->tsi]; + if (pt->is_destroyed) + return G_SOURCE_CONTINUE; + + lws_service_fd_tsi(sub->wsi->context, &eventfd, sub->wsi->tsi); + + if (!pt->glib.idle_tag) + lws_glib_set_idle(pt); + + if (pt->destroy_self) + lws_context_destroy(pt->context); + + return G_SOURCE_CONTINUE; +} + +static const GSourceFuncs lws_glib_source_ops = { + .prepare = NULL, + .check = lws_glib_check, + .dispatch = lws_glib_dispatch, + .finalize = NULL, +}; + +/* + * This is the callback for a timer object that is set to the earliest scheduled + * lws event... it services any lws scheduled events that are ready, and then + * resets the event loop timer to the earliest remaining event, if any. + */ + +static gboolean +lws_glib_hrtimer_cb(void *p) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; + unsigned int ms; + lws_usec_t us; + + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(&pt->pt_sul_owner, lws_now_usecs()); + if (us) { + ms = us / LWS_US_PER_MS; + if (!ms) + ms = 1; + + lws_glib_set_timeout(pt, ms); + } + + lws_pt_unlock(pt); + + lws_glib_set_idle(pt); + + return FALSE; /* stop it repeating */ +} + +static gboolean +lws_glib_idle_timer_cb(void *p) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; + + if (pt->is_destroyed) + return FALSE; + + lws_service_do_ripe_rxflow(pt); + lws_glib_hrtimer_cb(pt); + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) { + /* -1 timeout means just do forced service */ + _lws_plat_service_forced_tsi(pt->context, pt->tid); + /* still somebody left who wants forced service? */ + if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) + return TRUE; + } + + if (pt->destroy_self) + lws_context_destroy(pt->context); + + /* + * For glib, this disables the idle callback. Otherwise we keep + * coming back here immediately endlessly. + * + * We reenable the idle callback on the next network or scheduled event + */ + + pt->glib.idle_tag = 0; + + return FALSE; +} + +void +lws_glib_sigint_cb(void *ctx) +{ + struct lws_context_per_thread *pt = ctx; + + pt->inside_service = 1; + + if (pt->context->eventlib_signal_cb) { + pt->context->eventlib_signal_cb(NULL, 0); + + return; + } + if (!pt->event_loop_foreign) + g_main_loop_quit(pt_to_loop(pt)); +} + +static int +elops_init_context_glib(struct lws_context *context, + const struct lws_context_creation_info *info) +{ + int n; + + context->eventlib_signal_cb = info->signal_cb; + + for (n = 0; n < context->count_threads; n++) + context->pt[n].w_sigint.context = context; + + return 0; +} + +static int +elops_accept_glib(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int fd; + + assert(!wsi_to_subclass(wsi)); + + wsi_to_subclass(wsi) = (struct lws_io_watcher_glib_subclass *) + g_source_new((GSourceFuncs *)&lws_glib_source_ops, + sizeof(*wsi_to_subclass(wsi))); + if (!wsi_to_subclass(wsi)) + return 1; + + wsi->w_read.context = wsi->context; + wsi_to_subclass(wsi)->wsi = wsi; + + if (wsi->role_ops->file_handle) + fd = wsi->desc.filefd; + else + fd = wsi->desc.sockfd; + + wsi_to_subclass(wsi)->tag = g_source_add_unix_fd(wsi_to_gsource(wsi), + fd, (GIOCondition)LWS_POLLIN); + wsi->w_read.actual_events = LWS_POLLIN; + + g_source_set_callback(wsi_to_gsource(wsi), + G_SOURCE_FUNC(lws_service_fd), wsi->context, NULL); + + g_source_attach(wsi_to_gsource(wsi), pt_to_g_main_context(pt)); + + return 0; +} + +static int +elops_init_pt_glib(struct lws_context *context, void *_loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_vhost *vh = context->vhost_list; + GMainLoop *loop = (GMainLoop *)_loop; + + if (!loop) + loop = g_main_loop_new(NULL, 0); + else + context->pt[tsi].event_loop_foreign = 1; + + if (!loop) { + lwsl_err("%s: creating glib loop failed\n", __func__); + + return -1; + } + + pt->glib.loop = loop; + + /* + * Initialize all events with the listening sockets + * and register a callback for read operations + */ + + while (vh) { + if (vh->lserv_wsi) + elops_accept_glib(vh->lserv_wsi); + + vh = vh->vhost_next; + } + + lws_glib_set_idle(pt); + + /* Register the signal watcher unless it's a foreign loop */ + + if (pt->event_loop_foreign) + return 0; + + pt->glib.sigint_tag = g_unix_signal_add(SIGINT, + G_SOURCE_FUNC(lws_glib_sigint_cb), pt); + + return 0; +} + +/* + * We are changing the event wait for this guy + */ + +static void +elops_io_glib(struct lws *wsi, int flags) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + GIOCondition cond = wsi->w_read.actual_events | G_IO_ERR; + + if (!pt_to_loop(pt) || wsi->context->being_destroyed || pt->is_destroyed) + return; + + /* + * We are being given individual set / clear operations using + * LWS_EV_ common namespace, convert them to glib namespace bitfield + */ + + if (flags & LWS_EV_READ) { + if (flags & LWS_EV_STOP) + cond &= ~(G_IO_IN | G_IO_HUP); + else + cond |= G_IO_IN | G_IO_HUP; + } + + if (flags & LWS_EV_WRITE) { + if (flags & LWS_EV_STOP) + cond &= ~G_IO_OUT; + else + cond |= G_IO_OUT; + } + + wsi->w_read.actual_events = cond; + + lwsl_debug("%s: wsi %p, fd %d, 0x%x/0x%x\n", __func__, wsi, + wsi->desc.sockfd, flags, (int)cond); + + g_source_modify_unix_fd(wsi_to_gsource(wsi), wsi_to_subclass(wsi)->tag, + cond); +} + +static void +elops_run_pt_glib(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + + if (pt_to_loop(pt)) + g_main_loop_run(pt_to_loop(pt)); +} + +static void +elops_destroy_wsi_glib(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + + if (!wsi) + return; + + pt = &wsi->context->pt[(int)wsi->tsi]; + if (pt->is_destroyed) + return; + + if (!wsi_to_gsource(wsi)) + return; + + if (wsi_to_subclass(wsi)->tag) { + g_source_remove_unix_fd(wsi_to_gsource(wsi), + wsi_to_subclass(wsi)->tag); + wsi_to_subclass(wsi)->tag = NULL; + } + + g_source_destroy(wsi_to_gsource(wsi)); + wsi_to_subclass(wsi) = NULL; +} + +static void +elops_destroy_pt_glib(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_vhost *vh = context->vhost_list; + + if (!pt_to_loop(pt)) + return; + + /* + * Free all events with the listening sockets + */ + while (vh) { + if (vh->lserv_wsi) + elops_destroy_wsi_glib(vh->lserv_wsi); + + vh = vh->vhost_next; + } + + if (pt->glib.hrtimer_tag) + g_source_remove(pt->glib.hrtimer_tag); + + if (!pt->event_loop_foreign) { + g_main_loop_quit(pt_to_loop(pt)); + g_source_remove(pt->glib.sigint_tag); + g_main_loop_unref(pt_to_loop(pt)); + } + + pt_to_loop(pt) = NULL; +} + +static int +elops_destroy_context2_glib(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + int n; + + for (n = 0; n < (int)context->count_threads; n++) { + if (!pt->event_loop_foreign) + g_main_loop_quit(pt_to_loop(pt)); + pt++; + } + + return 0; +} + +static int +elops_wsi_logical_close_glib(struct lws *wsi) +{ + elops_destroy_wsi_glib(wsi); + + return 0; +} + +struct lws_event_loop_ops event_loop_ops_glib = { + /* name */ "glib", + /* init_context */ elops_init_context_glib, + /* destroy_context1 */ NULL, + /* destroy_context2 */ elops_destroy_context2_glib, + /* init_vhost_listen_wsi */ elops_accept_glib, + /* init_pt */ elops_init_pt_glib, + /* wsi_logical_close */ elops_wsi_logical_close_glib, + /* check_client_connect_ok */ NULL, + /* close_handle_manually */ NULL, + /* accept */ elops_accept_glib, + /* io */ elops_io_glib, + /* run_pt */ elops_run_pt_glib, + /* destroy_pt */ elops_destroy_pt_glib, + /* destroy wsi */ elops_destroy_wsi_glib, + + /* flags */ LELOF_DESTROY_FINAL, +}; diff --git a/lib/event-libs/glib/private-lib-event-libs-glib.h b/lib/event-libs/glib/private-lib-event-libs-glib.h new file mode 100644 index 000000000..b021ad6d9 --- /dev/null +++ b/lib/event-libs/glib/private-lib-event-libs-glib.h @@ -0,0 +1,54 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2020 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#if defined(LWS_WITH_GLIB) +#include +#endif /* LWS_WITH_GLIB */ + +struct lws_pt_eventlibs_glib { + GMainLoop *loop; + guint hrtimer_tag; + guint sigint_tag; + guint idle_tag; +}; + +struct lws_io_watcher_glib_subclass { + GSource base; + struct lws *wsi; + gpointer tag; +}; + +/* + * One of these is embedded in each wsi + */ + +struct lws_io_watcher_glib { + struct lws_io_watcher_glib_subclass *source; /* these are created and destroyed by glib */ +}; + +struct lws_context_eventlibs_glib { + //int placeholder; +}; + +extern struct lws_event_loop_ops event_loop_ops_glib; diff --git a/lib/event-libs/libev/libev.c b/lib/event-libs/libev/libev.c index 661e40247..77e410587 100644 --- a/lib/event-libs/libev/libev.c +++ b/lib/event-libs/libev/libev.c @@ -382,5 +382,5 @@ struct lws_event_loop_ops event_loop_ops_ev = { /* destroy_pt */ elops_destroy_pt_ev, /* destroy wsi */ elops_destroy_wsi_ev, - /* periodic_events_available */ 0, + /* flags */ 0, }; diff --git a/lib/event-libs/libevent/libevent.c b/lib/event-libs/libevent/libevent.c index 4284dccd3..f64a0f9d1 100644 --- a/lib/event-libs/libevent/libevent.c +++ b/lib/event-libs/libevent/libevent.c @@ -453,5 +453,5 @@ struct lws_event_loop_ops event_loop_ops_event = { /* destroy_pt */ elops_destroy_pt_event, /* destroy wsi */ elops_destroy_wsi_event, - /* periodic_events_available */ 0, + /* flags */ 0, }; diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index ee8891036..378a5dc6f 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -976,5 +976,5 @@ struct lws_event_loop_ops event_loop_ops_uv = { /* destroy_pt */ elops_destroy_pt_uv, /* destroy wsi */ NULL, - /* periodic_events_available */ 0, + /* flags */ 0, }; diff --git a/lib/event-libs/poll/poll.c b/lib/event-libs/poll/poll.c index b31da990d..85ffdc546 100644 --- a/lib/event-libs/poll/poll.c +++ b/lib/event-libs/poll/poll.c @@ -42,5 +42,5 @@ struct lws_event_loop_ops event_loop_ops_poll = { /* destroy_pt */ NULL, /* destroy wsi */ NULL, - /* periodic_events_available */ 1, + /* flags */ LELOF_ISPOLL, }; diff --git a/lib/event-libs/private-lib-event-libs.h b/lib/event-libs/private-lib-event-libs.h index 119e84ea2..55f0169f4 100644 --- a/lib/event-libs/private-lib-event-libs.h +++ b/lib/event-libs/private-lib-event-libs.h @@ -24,6 +24,11 @@ * This is included from private-lib-core.h */ +enum lws_event_lib_ops_flags { + LELOF_ISPOLL = (1 >> 0), + LELOF_DESTROY_FINAL = (1 >> 1), +}; + struct lws_event_loop_ops { const char *name; /* event loop-specific context init during context creation */ @@ -54,7 +59,7 @@ struct lws_event_loop_ops { /* called just before wsi is freed */ void (*destroy_wsi)(struct lws *wsi); - unsigned int periodic_events_available:1; + uint8_t flags; }; /* bring in event libs private declarations */ @@ -71,6 +76,10 @@ struct lws_event_loop_ops { #include "private-lib-event-libs-libevent.h" #endif +#if defined(LWS_WITH_GLIB) +#include "private-lib-event-libs-glib.h" +#endif + #if defined(LWS_WITH_LIBEV) #include "private-lib-event-libs-libev.h" #endif diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index d04fdb968..fa53788a9 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -2341,8 +2341,8 @@ lws_http_transaction_completed(struct lws *wsi) * until we can verify POLLOUT. The part of this that confirms POLLOUT * with no partials is in lws_server_socket_service() below. */ - lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__, - wsi, (int)wsi->wsistate); + lwsl_debug("%s: %p: setting DEF_ACT from 0x%x: %p\n", __func__, + wsi, (int)wsi->wsistate, wsi->buflist); lwsi_set_state(wsi, LRS_DEFERRING_ACTION); wsi->http.tx_content_length = 0; wsi->http.tx_content_remain = 0; diff --git a/minimal-examples/gtk/minimal-gtk/CMakeLists.txt b/minimal-examples/gtk/minimal-gtk/CMakeLists.txt new file mode 100644 index 000000000..fa4e46d35 --- /dev/null +++ b/minimal-examples/gtk/minimal-gtk/CMakeLists.txt @@ -0,0 +1,102 @@ +project(lws-minimal-gtk) +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-gtk) +set(SRCS main.c) + + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITH_SERVER 1 requirements) +require_lws_config(LWS_WITH_GLIB 1 requirements) +require_lws_config(LWS_WITH_GTK 1 requirements) + +if (requirements) + +# gtk pieces + + include (FindPkgConfig) + + set(LWS_GTK_INCLUDE_DIRS CACHE PATH "Path to the gtk include directory") + set(LWS_GTK_LIBRARIES CACHE PATH "Path to the gtk library") + PKG_SEARCH_MODULE(LWS_GTK2 gtk+-3.0) + if (LWS_GTK2_FOUND) + list(APPEND LWS_GTK_INCLUDE_DIRS "${LWS_GTK2_INCLUDE_DIRS}") + list(APPEND LWS_GTK_LIBRARIES "${LWS_GTK2_LIBRARIES}") + endif() + message("gtk include dir: ${LWS_GTK_INCLUDE_DIRS}") + message("gtk libraries: ${LWS_GTK_LIBRARIES}") + include_directories("${LWS_GTK_INCLUDE_DIRS}") + set(extralibs ${extralibs} ${LWS_GTK_LIBRARIES}) + + + + message("Extra libs: ${extralibs}") + + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared ${extralibs}) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets ${extralibs}) + endif() +endif() diff --git a/minimal-examples/gtk/minimal-gtk/README.md b/minimal-examples/gtk/minimal-gtk/README.md new file mode 100644 index 000000000..f85e5944b --- /dev/null +++ b/minimal-examples/gtk/minimal-gtk/README.md @@ -0,0 +1,32 @@ +# lws minimal http client gtk + +The application goes to https://warmcat.com and receives the page data, +from inside a gtk app using gtk / glib main loop directly. + +## build + +``` + $ cmake . && make +``` + +## usage + + +``` +$ +t1_main: started +[2020/02/08 18:04:07:6647] N: Loading client CA for verification ./warmcat.com.cer +[2020/02/08 18:04:07:7744] U: Connected to 46.105.127.147, http response: 200 +[2020/02/08 18:04:07:7762] U: RECEIVE_CLIENT_HTTP_READ: read 4087 +[2020/02/08 18:04:07:7762] U: RECEIVE_CLIENT_HTTP_READ: read 4096 +[2020/02/08 18:04:07:7928] U: RECEIVE_CLIENT_HTTP_READ: read 4087 +[2020/02/08 18:04:07:7929] U: RECEIVE_CLIENT_HTTP_READ: read 4096 +[2020/02/08 18:04:07:7956] U: RECEIVE_CLIENT_HTTP_READ: read 4087 +[2020/02/08 18:04:07:7956] U: RECEIVE_CLIENT_HTTP_READ: read 4096 +[2020/02/08 18:04:07:7956] U: RECEIVE_CLIENT_HTTP_READ: read 1971 +[2020/02/08 18:04:07:7956] U: LWS_CALLBACK_COMPLETED_CLIENT_HTTP +Hello World +$ +``` + + diff --git a/minimal-examples/gtk/minimal-gtk/main.c b/minimal-examples/gtk/minimal-gtk/main.c new file mode 100644 index 000000000..00860b489 --- /dev/null +++ b/minimal-examples/gtk/minimal-gtk/main.c @@ -0,0 +1,210 @@ +#include +#include + +static int status = 0; + +static void +print_hello(GtkWidget *widget, gpointer data) +{ + g_print("Hello World\n"); +} + +static void +activate(GtkApplication *app, gpointer user_data) +{ + GtkWidget *window; + GtkWidget *button, *bbox; + + window = gtk_application_window_new(app); + gtk_window_set_title(GTK_WINDOW(window), "mywindow"); + gtk_window_set_default_size(GTK_WINDOW(window), 200, 200); + + bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); + gtk_container_add(GTK_CONTAINER(window), bbox); + + button = gtk_button_new_with_label("Hello World"); + g_signal_connect(button, "clicked", G_CALLBACK(print_hello), NULL); + g_signal_connect_swapped(button, "clicked", + G_CALLBACK(gtk_widget_destroy), window); + gtk_container_add(GTK_CONTAINER(bbox), button); + + gtk_widget_show_all(window); +} + +static int +system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, + int current, int target) +{ + struct lws_context *context = mgr->parent; + struct lws_client_connect_info i; + + if (current != LWS_SYSTATE_OPERATIONAL || + target != LWS_SYSTATE_OPERATIONAL) + return 0; + + lwsl_notice("%s: operational\n", __func__); + + memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ + i.context = context; + i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | + LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; + i.port = 443; + i.address = "warmcat.com"; + i.path = "/"; + i.host = i.address; + i.origin = i.address; + i.method = "GET"; + + i.protocol = "http"; + + return !lws_client_connect_via_info(&i); +} + +static int +callback_http(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + switch (reason) { + + /* because we are protocols[0] ... */ + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", + in ? (char *)in : "(null)"); + break; + + case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: + { + char buf[128]; + + lws_get_peer_simple(wsi, buf, sizeof(buf)); + status = lws_http_client_http_response(wsi); + + lwsl_user("Connected to %s, http response: %d\n", + buf, status); + } + 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); + 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: + lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); + lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ + break; + + case LWS_CALLBACK_CLOSED_CLIENT_HTTP: + lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ + 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 } +}; + +static gpointer +t1_main (gpointer user_data) +{ + lws_state_notify_link_t notifier = { {}, system_notify_cb, "app" }; + lws_state_notify_link_t *na[] = { ¬ifier, NULL }; + GMainContext *t1_mc = (GMainContext *)user_data; + struct lws_context_creation_info info; + struct lws_context *context; + void *foreign_loops[1]; + GMainLoop *ml; + + g_print("%s: started\n", __func__); + + g_main_context_push_thread_default(t1_mc); + + ml = g_main_loop_new(t1_mc, FALSE); + + /* attach our lws activities to the main loop of this thread */ + + lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, NULL); + memset(&info, 0, sizeof info); + info.port = CONTEXT_PORT_NO_LISTEN; + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_GLIB; + info.protocols = protocols; + foreign_loops[0] = (void *)ml; + info.foreign_loops = foreign_loops; + info.register_notifier_list = na; + +#if defined(LWS_WITH_MBEDTLS) + /* + * OpenSSL uses the system trust store. mbedTLS has to be told which + * CA to trust explicitly. + */ + info.client_ssl_ca_filepath = "./warmcat.com.cer"; +#endif + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return NULL; + } + + /* + * We created the lws_context and bound it to this thread's main loop, + * let's run the thread's main loop now... + */ + + g_main_loop_run(ml); + g_main_loop_unref(ml); + + g_main_context_pop_thread_default(t1_mc); + g_main_context_unref(t1_mc); + + g_print("%s: ending\n", __func__); + + lws_context_destroy(context); + + return NULL; +} + +int +main(int argc, char **argv) +{ + GMainContext *t1_mc = g_main_context_new(); + GtkApplication *app; + GThread *t1; + int status; + + t1 = g_thread_new ("t1", t1_main, g_main_context_ref (t1_mc)); + (void)t1; + + app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE); + g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); + + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + + return status; +} + diff --git a/minimal-examples/gtk/minimal-gtk/warmcat.com.cer b/minimal-examples/gtk/minimal-gtk/warmcat.com.cer new file mode 100644 index 000000000..550393df7 --- /dev/null +++ b/minimal-examples/gtk/minimal-gtk/warmcat.com.cer @@ -0,0 +1,58 @@ +-----BEGIN CERTIFICATE----- +MIIFUDCCBDigAwIBAgISA4mJfIm3iCGbU9+o8YQa+4nUMA0GCSqGSIb3DQEBCwUA +MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD +ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5MjNaFw0x +OTEyMDYwNzA5MjNaMBYxFDASBgNVBAMTC3dhcm1jYXQuY29tMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwnEoH9JW3GvpadpxHGZPb5wv1Q6KfAIMWtdq +YCOfotFxaYULuzHVxmrTTgmEqJr+eBqUBkXKmGuRR/9UipOmTu5j02qFyWHotFdF +ZGyp//8z+Rle9Qt1nL68oNIZLDtWkybh5x00b1uo4eyEszXUaa0aLqKP3lH7Q4jI +aSVARZ8snrJR640Gp3ByudvNTYkGz469bpWzRC/8wSNtzzY02DvHs1GxQx9tMXw+ +BbtUxeP7lpYFKEFBjgZaIKLv+4g8ItJIuO7gMSzG2JfpQHxdhrlhxpx7dsaMUcyM +nnYXysNL5JG3KEMhkxbtdpCaEQ8jLSPbl/rnF/+mgce+lSjMuQIDAQABo4ICYjCC +Al4wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD +AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSI9ai12zLFeNTEDHKI9Ghkqcpa2TAf +BgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEw +LgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcw +LwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcv +MBYGA1UdEQQPMA2CC3dhcm1jYXQuY29tMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcG +CysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5 +cHQub3JnMIIBBgYKKwYBBAHWeQIEAgSB9wSB9ADyAHcAY/Lbzeg7zCzPC3KEJ1dr +M6SNYXePvXWmOLHHaFRL2I0AAAFtCsVHHAAABAMASDBGAiEAy0q1cR4VwPL3iviL +cBWN67kjJRXk+DwhodmeoM3kb3gCIQC2soAHFs0Umo+0RNdFrL41+hMuidh2cXbb +Ovc6nh5tOQB3AOJpS64m6OlACeiGG7Y7g9Q+5/50iPukjyiTAZ3d8dv+AAABbQrF +R48AAAQDAEgwRgIhANqKQm4t9by263CJ7/DLOaZCjtcK29KgJjPwhv08UMn1AiEA +h35nGTASR8/E7xz+56ZUleqD7U1ABFgWZRZskIzsFO8wDQYJKoZIhvcNAQELBQAD +ggEBADDJBVbKe2LPHmi8k2vxErB3Y0Ty+3gwgPEXKYtEvQ7tos89eE+QmOXAzH5J +GwRarFf7kzmKeJv04tMebiEtshpap47oJfxCxfrtpja8hP8Cdu/v/Ae6eEzu3yet +0N08GJdxQKfgCFaoGUptbaF2RCIZS12SVcX4TPpdP+xaiZdmIx4dGM6tReQ8+y8B +10b4Hi2+d/zW0W1z6+FAemU6yleWriJDUik5oas9XZF5LAAMDb/WgF5eIB6P9CUG +LuAO8lWlk9nBgXvMLTxZ74SJb17H4kFEIrIjvABNshz5gBW8xw9nfr5YIfANtwEj +BDsq06Df3UORYVs/j3T97gPAEZ4= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- diff --git a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c index 8c6580694..a6ada9a0d 100644 --- a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -342,7 +342,10 @@ int main(int argc, const char **argv) if (lws_cmdline_option(argc, argv, "--ev")) info.options |= LWS_SERVER_OPTION_LIBEV; else - signal(SIGINT, sigint_handler); + if (lws_cmdline_option(argc, argv, "--glib")) + info.options |= LWS_SERVER_OPTION_GLIB; + else + signal(SIGINT, sigint_handler); staggered = !!lws_cmdline_option(argc, argv, "-s"); if ((p = lws_cmdline_option(argc, argv, "-d"))) diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c b/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c index eaad5805d..abbe00da3 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c +++ b/minimal-examples/http-server/minimal-http-server-eventlib-demos/minimal-http-server-eventlib-demos.c @@ -170,7 +170,10 @@ int main(int argc, const char **argv) if (lws_cmdline_option(argc, argv, "--ev")) info.options |= LWS_SERVER_OPTION_LIBEV; else - signal(SIGINT, sigint_handler); + if (lws_cmdline_option(argc, argv, "--glib")) + info.options |= LWS_SERVER_OPTION_GLIB; + else + signal(SIGINT, sigint_handler); context = lws_create_context(&info); if (!context) { diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt index 197e50737..748a6b849 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt @@ -5,6 +5,7 @@ include(CheckCSourceCompiles) set(SAMP lws-minimal-http-server-eventlib-foreign) set(SRCS minimal-http-server-eventlib-foreign.c) + # If we are being built as part of lws, confirm current build config supports # reqconfig, else skip building ourselves. # @@ -66,9 +67,13 @@ set(requirements 1) require_lws_config(LWS_ROLE_H1 1 requirements) require_lws_config(LWS_WITH_SERVER 1 requirements) + + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBUV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBUV) CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEVENT)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEVENT) CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEV) +CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_GLIB)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_GLIB) + if (LWS_WITH_LIBUV) set(extralibs ${extralibs} uv) @@ -79,10 +84,33 @@ endif() if (LWS_WITH_LIBEV) set(extralibs ${extralibs} ev) endif() +if (LWS_WITH_GLIB) + set(LWS_GLIB_INCLUDE_DIRS CACHE PATH "Path to the glib include directory") + set(LWS_GLIB_LIBRARIES CACHE PATH "Path to the glib library") + include (FindPkgConfig) + if (NOT GLIB_FOUND) + find_path(GLIB_INCLUDE_DIRS NAMES glib-2.0/glib.h) + find_library(GLIB_LIBRARIES NAMES glib-2.0) + if(GLIB_INCLUDE_DIRS AND GLIB_LIBRARIES) + set(GLIB_FOUND 1) + endif() + if (GLIB_INCLUDE_DIRS) + set(GLIB_INCLUDE_DIRS "${GLIB_INCLUDE_DIRS}/glib-2.0") + endif() + endif() + PKG_SEARCH_MODULE(LWS_GLIB2 glib-2.0) + if (LWS_GLIB2_FOUND) + list(APPEND GLIB_INCLUDE_DIRS "${LWS_GLIB2_INCLUDE_DIRS}") + endif() + message("glib include dir: ${GLIB_INCLUDE_DIRS}") + message("glib libraries: ${GLIB_LIBRARIES}") + include_directories("${GLIB_INCLUDE_DIRS}") + set(extralibs ${extralibs} ${GLIB_LIBRARIES}) +endif() message("Extra libs: ${extralibs}") -if (NOT LWS_WITH_LIBUV AND NOT LWS_WITH_LIBEVENT AND NOT LWS_WITH_LIBEV) +if (NOT LWS_WITH_LIBUV AND NOT LWS_WITH_LIBEVENT AND NOT LWS_WITH_LIBEV AND NOT LWS_WITH_GLIB) set(requirements 0) endif() diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c index 053f1bf65..a5735e162 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c @@ -1,7 +1,7 @@ /* * lws-minimal-http-server-eventlib-foreign * - * Written in 2010-2019 by Andy Green + * Written in 2010-2020 by Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. @@ -205,6 +205,59 @@ foreign_event_loop_cleanup_libevent(void) #endif +#if defined(LWS_WITH_GLIB) + +#include +#include + +static GMainLoop *loop_glib; +static guint timer_outer_glib; +static guint sighandler_glib; + +static int +timer_cb_glib(void *p) +{ + foreign_timer_service(loop_glib); + return 1; +} + +static void +signal_cb_glib(void *p) +{ + signal_cb(SIGINT); +} + +static void +foreign_event_loop_init_and_run_glib(void) +{ + /* we create and start our "foreign loop" */ + + loop_glib = g_main_loop_new(NULL, 0); + + sighandler_glib = g_unix_signal_add(SIGINT, + G_SOURCE_FUNC(signal_cb_glib), NULL); + + timer_outer_glib = g_timeout_add(1000, timer_cb_glib, NULL); + + g_main_loop_run(loop_glib); +} + +static void +foreign_event_loop_stop_glib(void) +{ + g_main_loop_quit(loop_glib); +} + +static void +foreign_event_loop_cleanup_glib(void) +{ + /* cleanup the foreign loop assets */ + g_source_remove(sighandler_glib); + g_main_loop_unref(loop_glib); +} + +#endif + #if defined(LWS_WITH_LIBEV) static struct ev_loop *loop_ev; @@ -315,6 +368,10 @@ foreign_timer_service(void *foreign_loop) #if defined(LWS_WITH_LIBEV) if (info.options & LWS_SERVER_OPTION_LIBEV) foreign_event_loop_stop_libev(); +#endif +#if defined(LWS_WITH_GLIB) + if (info.options & LWS_SERVER_OPTION_GLIB) + foreign_event_loop_stop_glib(); #endif break; default: @@ -368,9 +425,12 @@ int main(int argc, const char **argv) else if (lws_cmdline_option(argc, argv, "--ev")) info.options |= LWS_SERVER_OPTION_LIBEV; - else { + else + if (lws_cmdline_option(argc, argv, "--glib")) + info.options |= LWS_SERVER_OPTION_GLIB; + else { lwsl_err("This app only makes sense when used\n"); - lwsl_err(" with a foreign loop, --uv, --event, or --ev\n"); + lwsl_err(" with a foreign loop, --uv, --event, --glib, or --ev\n"); return 1; } @@ -401,6 +461,10 @@ int main(int argc, const char **argv) if (info.options & LWS_SERVER_OPTION_LIBEV) foreign_event_loop_init_and_run_libev(); #endif +#if defined(LWS_WITH_GLIB) + if (info.options & LWS_SERVER_OPTION_GLIB) + foreign_event_loop_init_and_run_glib(); +#endif lws_context_destroy(context); @@ -418,6 +482,10 @@ int main(int argc, const char **argv) if (info.options & LWS_SERVER_OPTION_LIBEV) foreign_event_loop_cleanup_libev(); #endif +#if defined(LWS_WITH_LIBEV) + if (info.options & LWS_SERVER_OPTION_GLIB) + foreign_event_loop_cleanup_glib(); +#endif lwsl_user("%s: exiting...\n", __func__); diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c b/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c index 7e166e608..6ea9b6eac 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c +++ b/minimal-examples/http-server/minimal-http-server-eventlib-smp/minimal-http-server-eventlib-smp.c @@ -130,7 +130,10 @@ int main(int argc, const char **argv) if (lws_cmdline_option(argc, argv, "--ev")) info.options |= LWS_SERVER_OPTION_LIBEV; else - signal(SIGINT, sigint_handler); + if (lws_cmdline_option(argc, argv, "--glib")) + info.options |= LWS_SERVER_OPTION_GLIB; + else + signal(SIGINT, sigint_handler); context = lws_create_context(&info); if (!context) { diff --git a/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c b/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c index 9c2d49c8c..c3147f797 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c +++ b/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c @@ -101,7 +101,10 @@ int main(int argc, const char **argv) if (lws_cmdline_option(argc, argv, "--ev")) info.options |= LWS_SERVER_OPTION_LIBEV; else - signal(SIGINT, sigint_handler); + if (lws_cmdline_option(argc, argv, "--glib")) + info.options |= LWS_SERVER_OPTION_GLIB; + else + signal(SIGINT, sigint_handler); context = lws_create_context(&info); if (!context) {