plugins
authorAndy Green <andy@warmcat.com>
Wed, 6 Apr 2016 08:15:40 +0000 (16:15 +0800)
committerAndy Green <andy@warmcat.com>
Thu, 7 Apr 2016 01:38:08 +0000 (09:38 +0800)
This adds support for dynamically loaded plugins at runtime, which
can expose their own protocols or extensions transparently.

With these changes lwsws defaults to OFF in cmake, and if enabled it
automatically enables plugins and libuv support.

Signed-off-by: Andy Green <andy@warmcat.com>
23 files changed:
.travis.yml
CMakeLists.txt
README.lwsws.md
lib/context.c
lib/libev.c
lib/libuv.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/lws-plat-unix.c
lib/pollfd.c
lib/private-libwebsockets.h
lib/server.c
lib/service.c
lws_config.h.in
lwsws/etc-lwsws-conf-EXAMPLE [new file with mode: 0644]
lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE [new file with mode: 0644]
lwsws/http.c
lwsws/main.c
plugins/protocol_dumb_increment.c [new file with mode: 0644]
plugins/protocol_lws_mirror.c [new file with mode: 0644]
plugins/protocol_lws_status.c [new file with mode: 0644]
test-server/test-server-libuv.c
travis_install.sh

index b61a765..56dd54f 100644 (file)
@@ -4,6 +4,7 @@ env:
   global:
     - secure: "KhAdQ9ja+LBObWNQTYO7Df5J4DyOih6S+eerDMu8UPSO+CoWV2pWoQzbOfocjyOscGOwC+2PrrHDNZyGfqkCLDXg1BxynXPCFerHC1yc2IajvKpGXmAAygNIvp4KACDfGv/dkXrViqIzr/CdcNaU4vIMHSVb5xkeLi0W1dPnQOI="
   matrix:
+    - LWS_METHOD=lwsws CMAKE_ARGS="-DLWS_WITH_LWSWS=ON"
     - LWS_METHOD=default
     - LWS_METHOD=noserver CMAKE_ARGS="-DLWS_WITHOUT_SERVER=ON"
     - LWS_METHOD=noclient CMAKE_ARGS="-DLWS_WITHOUT_CLIENT=ON"
@@ -18,16 +19,18 @@ env:
 os:
   - linux
   - osx
-language: c
+language: generic
 install:
   - ./travis_install.sh
 script:
   - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then mkdir build && cd build && cmake $CMAKE_ARGS .. && cmake --build .; fi
+sudo: required
+dist: trusty
 addons:
   coverity_scan:
     project:
       name: "warmcat/libwebsockets"
-    notification_email: andy.green@linaro.org
+    notification_email: andy@warmcat.com
     build_command_prepend: "mkdir build && cd build && cmake .."
     build_command:   "cmake --build ."
     branch_pattern: coverity_scan
index f45a70e..d12af4d 100644 (file)
@@ -91,7 +91,14 @@ option(LWS_MBED3 "Platform is MBED3" OFF)
 option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF)
 option(LWS_WITH_CGI "Include CGI (spawn process with network-connected stdin/out/err) APIs" OFF)
 option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying" OFF)
-option(LWS_WITH_LWSWS "Libwebsockets Webserver" ON)
+option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
+option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF)
+
+if (LWS_WITH_LWSWS)
+ message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV")
+ set(LWS_WITH_PLUGINS 1)
+ set(LWS_WITH_LIBUV 1)
+endif()
 
 if (DEFINED YOTTA_WEBSOCKETS_VERSION_STRING)
 
@@ -101,6 +108,7 @@ set(LWS_WITH_ZLIB OFF)
 set(LWS_WITHOUT_CLIENT ON)
 set(LWS_WITHOUT_TESTAPPS ON)
 set(LWS_WITHOUT_EXTENSIONS ON)
+set(LWS_WITH_PLUGINS OFF)
 set(LWS_MBED3 ON)
 # this implies no pthreads in the lib
 set(LWS_MAX_SMP 1)
@@ -110,6 +118,9 @@ endif()
 if (WIN32)
 # this implies no pthreads in the lib
 set(LWS_MAX_SMP 1)
+
+# plugin stuff not implemented in win32 plat
+set (LWS_WITH_PLUGINS OFF)
 endif()
 
 
@@ -926,6 +937,10 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                message("OpenSSL executable: ${OPENSSL_EXECUTABLE}")
        endif()
 
+       if (UNIX AND LWS_WITH_PLUGINS)
+               set(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
+               target_link_libraries(websockets dl)
+       endif()
 
        if (NOT LWS_WITHOUT_SERVER)
                #
@@ -1115,6 +1130,54 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                endif()
 
        endif(NOT LWS_WITHOUT_CLIENT)
+       
+       
+       if (LWS_WITH_PLUGINS AND LWS_WITH_SHARED)
+               macro(create_plugin PLUGIN_NAME MAIN_SRC)
+
+               set(PLUGIN_SRCS ${MAIN_SRC})
+
+               if (WIN32)
+                       list(APPEND PLUGIN_SRCSset_property
+                               ${WIN32_HELPERS_PATH}/getopt.c
+                               ${WIN32_HELPERS_PATH}/getopt_long.c
+                               ${WIN32_HELPERS_PATH}/gettimeofday.c
+                       )
+
+                       list(APPEND PLUGIN_HDR
+                               ${WIN32_HELPERS_PATH}/getopt.h
+                               ${WIN32_HELPERS_PATH}/gettimeofday.h
+                       )
+               endif(WIN32)
+
+               source_group("Headers Private"   FILES ${PLUGIN_HDR})
+               source_group("Sources"   FILES ${PLUGIN_SRCS})
+               add_library(${PLUGIN_NAME} SHARED ${PLUGIN_SRCS} ${PLUGIN_HDR})
+               
+               target_link_libraries(${PLUGIN_NAME} websockets)
+               add_dependencies(${PLUGIN_NAME} websockets)
+
+               # Set test app specific defines.
+               set_property(TARGET ${PLUGIN_NAME}
+                            PROPERTY COMPILE_DEFINITIONS
+                            INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/plugins"
+               )
+
+#              set_target_properties(${PLUGIN_NAME}
+#                      PROPERTIES
+#                      OUTPUT_NAME ${PLUGIN_NAME})
+
+               list(APPEND PLUGINS_LIST ${PLUGIN_NAME})
+               endmacro()
+               
+
+               create_plugin(protocol_dumb_increment
+                             "plugins/protocol_dumb_increment.c")
+               create_plugin(protocol_lws_mirror
+                             "plugins/protocol_lws_mirror.c")
+               create_plugin(protocol_lws_status
+                             "plugins/protocol_lws_status.c")
+       endif(LWS_WITH_PLUGINS AND LWS_WITH_SHARED)
 
        #
        # Copy OpenSSL dlls to the output directory on Windows.
@@ -1318,6 +1381,15 @@ if (LWS_WITH_CGI)
        endif()
 endif()
 
+# plugins
+
+if (LWS_WITH_PLUGINS)
+       install(TARGETS ${PLUGINS_LIST}
+               PERMISSIONS  OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE OWNER_READ GROUP_READ WORLD_READ
+               DESTINATION share/libwebsockets-test-server/plugins
+               COMPONENT plugins)
+endif()
+
 # Install the LibwebsocketsConfig.cmake and LibwebsocketsConfigVersion.cmake
 install(FILES
                "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LibwebsocketsConfig.cmake"
@@ -1379,6 +1451,7 @@ message(" LWS_WITH_CGI = ${LWS_WITH_CGI}")
 message(" LWS_HAVE_OPENSSL_ECDH_H = ${LWS_HAVE_OPENSSL_ECDH_H}")
 message(" LWS_WITH_HTTP_PROXY = ${LWS_WITH_HTTP_PROXY}")
 message(" LIBHUBBUB_LIBRARIES = ${LIBHUBBUB_LIBRARIES}")
+message(" PLUGINS = ${PLUGINS_LIST}")
 message("---------------------------------------------------------------------")
 
 # These will be available to parent projects including libwebsockets using add_subdirectory()
index 460e0e5..ff559b9 100644 (file)
@@ -4,6 +4,14 @@ Libwebsockets Web Server
 lwsws is an implementation of a very lightweight, ws-capable generic web
 server, which uses libwebsockets to implement everything underneath.
 
+Build
+-----
+
+Just enable -DLWS_WITH_LWSWS=1 at cmake-time.
+
+It enables libuv and plugin support automatically.
+
+
 Configuration
 -------------
 
@@ -94,10 +102,12 @@ The vhost name field is used to match on incoming SNI or Host: header, so it
 must always be the host name used to reach the vhost externally.
 
 Vhosts may have the same name and different ports, these will each create a
-listening socket on the appropriate port, and they may have the same port and
-different name: these will be treated as true vhosts on one listening socket
-and the active vhost decided at SSL negotiation time (via SNI) or if no SSL,
-then after the Host: header from the client has been parsed.
+listening socket on the appropriate port.
+
+They may also have the same port and different name: these will be treated as
+true vhosts on one listening socket and the active vhost decided at SSL
+negotiation time (via SNI) or if no SSL, then after the Host: header from
+the client has been parsed.
 
 
 Mounts
@@ -107,4 +117,34 @@ Where mounts are given in the vhost definition, then directory contents may
 be auto-served if it matches the mountpoint.
 
 Currently only file:// mount protocol and a fixed set of mimetypes are
-supported.
\ No newline at end of file
+supported.
+
+
+Plugins
+-------
+
+Protcols and extensions may also be provided from "plugins", these are
+lightweight dynamic libraries.  They are scanned for at init time, and
+any protocols and extensions found are added to the list given at context
+creation time.
+
+Protocols receive init (LWS_CALLBACK_PROTOCOL_INIT) and destruction
+(LWS_CALLBACK_PROTOCOL_DESTROY) callbacks per-vhost, and there are arrangements
+they can make per-vhost allocations and get hold of the correct pointer from
+the wsi at the callback.
+
+This allows a protocol to choose to strictly segregate data on a per-vhost
+basis, and also allows the plugin to handle its own initialization and
+context storage.
+
+To help that happen conveniently, there are some new apis
+
+ - lws_vhost_get(wsi)
+ - lws_protocol_get(wsi)
+ - lws_callback_on_writable_all_protocol_vhost(vhost, protocol)
+ - lws_protocol_vh_priv_zalloc(vhost, protocol, size)
+ - lws_protocol_vh_priv_get(vhost, protocol)
+dumb increment, mirror and status protocol plugins are provided as examples.
+
+
index b8d031b..46ff505 100644 (file)
@@ -86,6 +86,86 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
        return ((char *)store + sizeof(*m)) - (char *)orig;
 }
 
+LWS_VISIBLE void *
+lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot,
+                           int size)
+{
+       int n = 0;
+
+       /* allocate the vh priv array only on demand */
+       if (!vhost->protocol_vh_privs) {
+               vhost->protocol_vh_privs = (void **)lws_zalloc(
+                               vhost->count_protocols * sizeof(void *));
+               if (!vhost->protocol_vh_privs)
+                       return NULL;
+       }
+
+       while (n < vhost->count_protocols && &vhost->protocols[n] != prot)
+               n++;
+
+       if (n == vhost->count_protocols)
+               return NULL;
+
+       vhost->protocol_vh_privs[n] = lws_zalloc(size);
+       return vhost->protocol_vh_privs[n];
+}
+
+LWS_VISIBLE void *
+lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot)
+{
+       int n = 0;
+
+       if (!vhost->protocol_vh_privs)
+               return NULL;
+
+       while (n < vhost->count_protocols && &vhost->protocols[n] != prot)
+               n++;
+
+       if (n == vhost->count_protocols) {
+               lwsl_err("%s: unknown protocol %p\n", __func__, prot);
+               return NULL;
+       }
+
+       return vhost->protocol_vh_privs[n];
+}
+
+int
+lws_protocol_init(struct lws_context *context)
+{
+       struct lws_vhost *vh = context->vhost_list;
+       struct lws wsi;
+       int n;
+
+       memset(&wsi, 0, sizeof(wsi));
+       wsi.context = context;
+
+       while (vh) {
+               wsi.vhost = vh;
+
+               /* initialize supported protocols on this vhost */
+
+               for (n = 0; n < vh->count_protocols; n++) {
+                       wsi.protocol = &vh->protocols[n];
+
+                       /*
+                        * inform all the protocols that they are doing their one-time
+                        * initialization if they want to.
+                        *
+                        * NOTE the wsi is all zeros except for the context, vh and
+                        * protocol ptrs so lws_get_context(wsi) etc can work
+                        */
+                       vh->protocols[n].callback(&wsi,
+                               LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
+               }
+
+               vh = vh->vhost_next;
+       }
+
+       context->protocol_init_done = 1;
+
+       return 0;
+}
+
 LWS_VISIBLE struct lws_vhost *
 lws_create_vhost(struct lws_context *context,
                 struct lws_context_creation_info *info,
@@ -93,9 +173,12 @@ lws_create_vhost(struct lws_context *context,
 {
        struct lws_vhost *vh = lws_zalloc(sizeof(*vh)),
                         **vh1 = &context->vhost_list;
-       struct lws wsi;
+#ifdef LWS_WITH_PLUGINS
+       struct lws_plugin *plugin = context->plugin_list;
+       struct lws_protocols *lwsp;
+       int m;
+#endif
        char *p;
-       int n;
 
        if (!vh)
                return NULL;
@@ -107,11 +190,38 @@ lws_create_vhost(struct lws_context *context,
                vh->name = info->vhost_name;
 
        vh->iface = info->iface;
-       vh->protocols = info->protocols;
        for (vh->count_protocols = 0;
             info->protocols[vh->count_protocols].callback;
             vh->count_protocols++)
                ;
+#ifdef LWS_WITH_PLUGINS
+       if (plugin) {
+               /*
+                * give the vhost a unified list of protocols including the
+                * ones that came from plugins
+                */
+               lwsp = lws_zalloc(sizeof(struct lws_protocols) *
+                                          (vh->count_protocols +
+                                          context->plugin_protocol_count + 1));
+               if (!lwsp)
+                       return NULL;
+
+               m = vh->count_protocols;
+               memcpy(lwsp, info->protocols,
+                      sizeof(struct lws_protocols) * m);
+               while (plugin) {
+                       memcpy(&lwsp[m], plugin->caps.protocols,
+                              sizeof(struct lws_protocols) *
+                              plugin->caps.count_protocols);
+                       m += plugin->caps.count_protocols;
+                       vh->count_protocols += plugin->caps.count_protocols;
+                       plugin = plugin->list;
+               }
+               vh->protocols = lwsp;
+       } else
+#endif
+               vh->protocols = info->protocols;
+
 
        vh->mount_list = mounts;
 
@@ -126,7 +236,35 @@ lws_create_vhost(struct lws_context *context,
        }
 
 #ifndef LWS_NO_EXTENSIONS
-       vh->extensions = info->extensions;
+#ifdef LWS_WITH_PLUGINS
+       if (context->plugin_extension_count) {
+
+               m = 0;
+               while (info->extensions && info->extensions[m].callback)
+                       m++;
+
+               /*
+                * give the vhost a unified list of extensions including the
+                * ones that came from plugins
+                */
+               vh->extensions = lws_zalloc(sizeof(struct lws_extension) *
+                                          (m +
+                                          context->plugin_extension_count + 1));
+               if (!vh->extensions)
+                       return NULL;
+
+               memcpy((struct lws_extension *)vh->extensions, info->extensions,
+                      sizeof(struct lws_extension) * m);
+               while (plugin) {
+                       memcpy((struct lws_extension *)&vh->extensions[m], plugin->caps.extensions,
+                              sizeof(struct lws_extension) *
+                              plugin->caps.count_extensions);
+                       m += plugin->caps.count_extensions;
+                       plugin = plugin->list;
+               }
+       } else
+#endif
+               vh->extensions = info->extensions;
 #endif
 
        vh->listen_port = info->port;
@@ -148,23 +286,6 @@ lws_create_vhost(struct lws_context *context,
 #endif
        }
 
-       memset(&wsi, 0, sizeof(wsi));
-       wsi.context = context;
-       wsi.vhost = vh;
-
-       /* initialize supported protocols */
-
-       for (n = 0; n < vh->count_protocols; n++)
-               /*
-                * inform all the protocols that they are doing their one-time
-                * initialization if they want to.
-                *
-                * NOTE the wsi is all zeros except for the context & vh ptrs
-                * so lws_get_context(wsi) can work in the callback.
-                */
-               info->protocols[n].callback(&wsi,
-                               LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0);
-
        vh->ka_time = info->ka_time;
        vh->ka_interval = info->ka_interval;
        vh->ka_probes = info->ka_probes;
@@ -467,7 +588,7 @@ lws_context_destroy(struct lws_context *context)
 {
        const struct lws_protocols *protocol = NULL;
        struct lws_context_per_thread *pt;
-       struct lws_vhost *vh;
+       struct lws_vhost *vh, *vh1;
        struct lws wsi;
        int n, m;
 
@@ -514,18 +635,25 @@ lws_context_destroy(struct lws_context *context)
 
        /*
         * inform all the protocols that they are done and will have no more
-        * callbacks
+        * callbacks.
+        *
+        * We can't free things until after the event loop shuts down.
         */
        vh = context->vhost_list;
        while (vh) {
+               wsi.vhost = vh;
                protocol = vh->protocols;
-               if (protocol)
-                       while (protocol->callback) {
+               if (protocol) {
+                       n = 0;
+                       while (n < vh->count_protocols) {
+                               wsi.protocol = protocol;
                                protocol->callback(&wsi, LWS_CALLBACK_PROTOCOL_DESTROY,
                                                   NULL, NULL, 0);
                                protocol++;
+                               n++;
                        }
-               lws_ssl_SSL_CTX_destroy(vh);
+               }
+
                vh = vh->vhost_next;
        }
 
@@ -547,6 +675,39 @@ lws_context_destroy(struct lws_context *context)
        if (context->pt[0].fds)
                lws_free_set_NULL(context->pt[0].fds);
 
+       /* free all the vhost allocations */
+
+       vh = context->vhost_list;
+       while (vh) {
+               protocol = vh->protocols;
+               if (protocol) {
+                       n = 0;
+                       while (n < vh->count_protocols) {
+                               if (vh->protocol_vh_privs &&
+                                   vh->protocol_vh_privs[n]) {
+                                       lws_free(vh->protocol_vh_privs[n]);
+                                       vh->protocol_vh_privs[n] = NULL;
+                               }
+                               protocol++;
+                               n++;
+                       }
+               }
+               if (vh->protocol_vh_privs)
+                       lws_free(vh->protocol_vh_privs);
+               lws_ssl_SSL_CTX_destroy(vh);
+#ifdef LWS_WITH_PLUGINS
+               if (context->plugin_list)
+                       lws_free((void *)vh->protocols);
+#ifndef LWS_NO_EXTENSIONS
+               if (context->plugin_extension_count)
+                       lws_free((void *)vh->extensions);
+#endif
+#endif
+               vh1 = vh->vhost_next;
+               lws_free(vh);
+               vh = vh1;
+       }
+
        lws_plat_context_late_destroy(context);
 
        lws_free(context);
index cb8c3c5..39242bb 100644 (file)
@@ -32,7 +32,7 @@ void lws_feature_status_libev(struct lws_context_creation_info *info)
 static void
 lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
 {
-       struct lws_io_watcher *lws_io = container_of(watcher,
+       struct lws_io_watcher *lws_io = lws_container_of(watcher,
                                        struct lws_io_watcher, ev_watcher);
        struct lws_context *context = lws_io->context;
        struct lws_pollfd eventfd;
index 14d3f18..3cf4a94 100644 (file)
@@ -31,9 +31,13 @@ lws_feature_status_libuv(struct lws_context_creation_info *info)
 }
 
 static void
-lws_uv_idle(uv_idle_t *handle)
+lws_uv_idle(uv_idle_t *handle
+#if UV_VERSION_MAJOR == 0
+               , int status
+#endif
+)
 {
-       struct lws_context_per_thread *pt = container_of(handle,
+       struct lws_context_per_thread *pt = lws_container_of(handle,
                                        struct lws_context_per_thread, uv_idle);
 
        lwsl_debug("%s\n", __func__);
@@ -57,9 +61,9 @@ lws_uv_idle(uv_idle_t *handle)
 static void
 lws_io_cb(uv_poll_t *watcher, int status, int revents)
 {
-       struct lws_io_watcher *lws_io = container_of(watcher,
+       struct lws_io_watcher *lws_io = lws_container_of(watcher,
                                        struct lws_io_watcher, uv_watcher);
-       struct lws *wsi = container_of(lws_io, struct lws, w_read);
+       struct lws *wsi = lws_container_of(lws_io, struct lws, w_read);
        struct lws_context *context = lws_io->context;
        struct lws_pollfd eventfd;
 
@@ -117,9 +121,13 @@ lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint,
 }
 
 static void
-lws_uv_timeout_cb(uv_timer_t *timer)
+lws_uv_timeout_cb(uv_timer_t *timer
+#if UV_VERSION_MAJOR == 0
+               , int status
+#endif
+)
 {
-       struct lws_context_per_thread *pt = container_of(timer,
+       struct lws_context_per_thread *pt = lws_container_of(timer,
                        struct lws_context_per_thread, uv_timeout_watcher);
 
        lwsl_debug("%s\n", __func__);
@@ -138,7 +146,12 @@ lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi)
 
        if (!loop) {
                loop = lws_malloc(sizeof(*loop));
+#if UV_VERSION_MAJOR > 0
                uv_loop_init(loop);
+#else
+               lwsl_err("This libuv is too old to work...\n");
+               return 1;
+#endif
                pt->ev_loop_foreign = 0;
        } else
                pt->ev_loop_foreign = 1;
@@ -217,9 +230,11 @@ lws_libuv_destroyloop(struct lws_context *context, int tsi)
                uv_stop(pt->io_loop_uv);
                uv_walk(pt->io_loop_uv, lws_uv_walk_cb, NULL);
                while (uv_run(pt->io_loop_uv, UV_RUN_NOWAIT));
+#if UV_VERSION_MAJOR > 0
                m = uv_loop_close(pt->io_loop_uv);
                if (m == UV_EBUSY)
                        lwsl_debug("%s: uv_loop_close: UV_EBUSY\n", __func__);
+#endif
                lws_free(pt->io_loop_uv);
        }
 }
@@ -298,9 +313,8 @@ lws_libuv_init_fd_table(struct lws_context *context)
        if (!LWS_LIBUV_ENABLED(context))
                return 0;
 
-       for (n = 0; n < context->count_threads; n++) {
+       for (n = 0; n < context->count_threads; n++)
                context->pt[n].w_sigint.context = context;
-       }
 
        return 1;
 }
index b83b314..9958816 100644 (file)
@@ -707,6 +707,18 @@ lws_context_user(struct lws_context *context)
        return context->user_space;
 }
 
+LWS_VISIBLE struct lws_vhost *
+lws_vhost_get(struct lws *wsi)
+{
+       return wsi->vhost;
+}
+
+LWS_VISIBLE const struct lws_protocols *
+lws_protocol_get(struct lws *wsi)
+{
+       return wsi->protocol;
+}
+
 
 /**
  * lws_callback_all_protocol() - Callback all connections using
@@ -739,6 +751,39 @@ lws_callback_all_protocol(struct lws_context *context,
        return 0;
 }
 
+/**
+ * lws_callback_all_protocol_vhost() - Callback all connections using
+ *                             the given protocol with the given reason
+ *
+ * @vh:                Vhost whose connections will get callbacks
+ * @protocol:  Which protocol to match
+ * @reason:    Callback reason index
+ */
+
+LWS_VISIBLE int
+lws_callback_all_protocol_vhost(struct lws_vhost *vh,
+                         const struct lws_protocols *protocol, int reason)
+{
+       struct lws_context *context = vh->context;
+       struct lws_context_per_thread *pt = &context->pt[0];
+       unsigned int n, m = context->count_threads;
+       struct lws *wsi;
+
+       while (m--) {
+               for (n = 0; n < pt->fds_count; n++) {
+                       wsi = wsi_from_fd(context, pt->fds[n].fd);
+                       if (!wsi)
+                               continue;
+                       if (wsi->vhost == vh && wsi->protocol == protocol)
+                               protocol->callback(wsi, reason, wsi->user_space,
+                                                  NULL, 0);
+               }
+               pt++;
+       }
+
+       return 0;
+}
+
 #if LWS_POSIX
 
 /**
index 9479fce..5cbaaad 100644 (file)
@@ -280,6 +280,14 @@ LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len);
 #define lwsl_hexdump(a, b)
 
 #endif
+
+#include <stddef.h>
+
+#ifndef lws_container_of
+#define lws_container_of(P,T,M)        ((T *)((char *)(P) - offsetof(T, M)))
+#endif
+
+
 struct lws;
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
 
@@ -1293,6 +1301,26 @@ struct lws_extension {
         * This is part of the ABI, don't needlessly break compatibility */
 };
 
+#define LWS_PLUGIN_API_MAGIC 180
+
+struct lws_plugin_capability {
+       unsigned int api_magic; /* caller fills this in, plugin fills rest */
+       const struct lws_protocols *protocols;
+       int count_protocols;
+       const struct lws_extension *extensions;
+       int count_extensions;
+};
+
+typedef int (*lws_plugin_init_func)(struct lws_context *,
+                                   struct lws_plugin_capability *);
+typedef int (*lws_plugin_destroy_func)(struct lws_context *);
+struct lws_plugin {
+       struct lws_plugin *list;
+       void *l;
+       char name[64];
+       struct lws_plugin_capability caps;
+};
+
 /*
  * The internal exts are part of the public abi
  * If we add more extensions, publish the callback here  ------v
@@ -1414,6 +1442,7 @@ struct lws_context_creation_info {
        unsigned int timeout_secs;                      /* VH */
        const char *ecdh_curve;                         /* VH */
        const char *vhost_name;                         /* VH */
+       const char *plugins_dir;                        /* context */
 
        /* Add new things just above here ---^
         * This is part of the ABI, don't needlessly break compatibility
@@ -1505,6 +1534,18 @@ lws_create_vhost(struct lws_context *context,
                 struct lws_context_creation_info *info,
                 struct lws_http_mount *mounts);
 
+LWS_VISIBLE struct lws_vhost *
+lws_vhost_get(struct lws *wsi);
+
+LWS_VISIBLE const struct lws_protocols *
+lws_protocol_get(struct lws *wsi);
+
+LWS_VISIBLE void *
+lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot,
+                           int size);
+LWS_VISIBLE void *
+lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot);
+
 LWS_VISIBLE LWS_EXTERN int
 lws_finalize_startup(struct lws_context *context);
 
@@ -1740,10 +1781,18 @@ LWS_VISIBLE LWS_EXTERN int
 lws_callback_on_writable_all_protocol(const struct lws_context *context,
                                      const struct lws_protocols *protocol);
 
+LWS_VISIBLE int
+lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
+                                     const struct lws_protocols *protocol);
+
 LWS_VISIBLE LWS_EXTERN int
 lws_callback_all_protocol(struct lws_context *context,
                          const struct lws_protocols *protocol, int reason);
 
+LWS_VISIBLE int
+lws_callback_all_protocol_vhost(struct lws_vhost *vh,
+                         const struct lws_protocols *protocol, int reason);
+
 LWS_VISIBLE LWS_EXTERN int
 lws_get_socket_fd(struct lws *wsi);
 
index 2b89e22..c726e9e 100644 (file)
@@ -3,6 +3,10 @@
 #include <pwd.h>
 #include <grp.h>
 
+#include <dlfcn.h>
+#include <dirent.h>
+
+
 /*
  * included from libwebsockets.c for unix builds
  */
@@ -294,6 +298,141 @@ lws_plat_drop_app_privileges(struct lws_context_creation_info *info)
        }
 }
 
+#ifdef LWS_WITH_PLUGINS
+
+static int filter(const struct dirent *ent)
+{
+       if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
+               return 0;
+
+       return 1;
+}
+
+LWS_VISIBLE int
+lws_plat_plugins_init(struct lws_context * context, const char *d)
+{
+       struct lws_plugin_capability lcaps;
+       struct lws_plugin *plugin;
+       lws_plugin_init_func initfunc;
+       struct dirent **namelist;
+       int n, i, m, ret = 0;
+       char path[256];
+       void *l;
+
+
+       n = scandir(d, &namelist, filter, alphasort);
+       if (n < 0) {
+               lwsl_err("Scandir on %d failed\n", d);
+               return 1;
+       }
+
+       lwsl_notice("  Plugins:\n");
+
+       for (i = 0; i < n; i++) {
+               if (strlen(namelist[i]->d_name) < 7)
+                       goto inval;
+
+               lwsl_notice("   %s\n", namelist[i]->d_name);
+
+               snprintf(path, sizeof(path) - 1, "%s/%s", d,
+                        namelist[i]->d_name);
+               l = dlopen(path, RTLD_NOW);
+               if (!l) {
+                       lwsl_err("Error loading DSO: %s\n", dlerror());
+                       while (i++ < n)
+                               free(namelist[i]);
+                       goto bail;
+               }
+               /* we could open it, can we get his init function? */
+               m = snprintf(path, sizeof(path) - 1, "init_%s",
+                            namelist[i]->d_name + 3 /* snip lib... */);
+               path[m - 3] = '\0'; /* snip the .so */
+               initfunc = dlsym(l, path);
+               if (!initfunc) {
+                       lwsl_err("Failed to get init on %s: %s",
+                                       namelist[i]->d_name, dlerror());
+                       dlclose(l);
+               }
+               lcaps.api_magic = LWS_PLUGIN_API_MAGIC;
+               m = initfunc(context, &lcaps);
+               if (m) {
+                       lwsl_err("Initializing %s failed %d\n",
+                               namelist[i]->d_name, m);
+                       dlclose(l);
+                       goto skip;
+               }
+
+               plugin = lws_malloc(sizeof(*plugin));
+               if (!plugin) {
+                       lwsl_err("OOM\n");
+                       goto bail;
+               }
+               plugin->list = context->plugin_list;
+               context->plugin_list = plugin;
+               strncpy(plugin->name, namelist[i]->d_name, sizeof(plugin->name) - 1);
+               plugin->name[sizeof(plugin->name) - 1] = '\0';
+               plugin->l = l;
+               plugin->caps = lcaps;
+               context->plugin_protocol_count += lcaps.count_protocols;
+               context->plugin_extension_count += lcaps.count_extensions;
+
+               free(namelist[i]);
+               continue;
+
+skip:
+               dlclose(l);
+inval:
+               free(namelist[i]);
+       }
+
+bail:
+       free(namelist);
+
+       return ret;
+}
+
+LWS_VISIBLE int
+lws_plat_plugins_destroy(struct lws_context * context)
+{
+       struct lws_plugin *plugin = context->plugin_list, *p;
+       lws_plugin_destroy_func func;
+       char path[256];
+       int m;
+
+       if (!plugin)
+               return 0;
+
+       lwsl_notice("%s\n", __func__);
+
+       while (plugin) {
+               p = plugin;
+               m = snprintf(path, sizeof(path) - 1, "destroy_%s", plugin->name + 3);
+               path[m - 3] = '\0';
+               func = dlsym(plugin->l, path);
+               if (!func) {
+                       lwsl_err("Failed to get destroy on %s: %s",
+                                       plugin->name, dlerror());
+                       goto next;
+               }
+               m = func(context);
+               if (m)
+                       lwsl_err("Initializing %s failed %d\n",
+                               plugin->name, m);
+next:
+               dlclose(p->l);
+               plugin = p->list;
+               p->list = NULL;
+               free(p);
+       }
+
+       context->plugin_list = NULL;
+
+       return 0;
+}
+
+#endif
+
+
 static void
 sigpipe_handler(int x)
 {
@@ -326,6 +465,11 @@ lws_plat_context_late_destroy(struct lws_context *context)
        struct lws_context_per_thread *pt = &context->pt[0];
        int m = context->count_threads;
 
+#ifdef LWS_WITH_PLUGINS
+       if (context->plugin_list)
+               lws_plat_plugins_destroy(context);
+#endif
+
        if (context->lws_lookup)
                lws_free(context->lws_lookup);
 
@@ -513,6 +657,7 @@ _lws_plat_file_write(struct lws *wsi, lws_filefd_type fd, unsigned long *amount,
        return 0;
 }
 
+
 LWS_VISIBLE int
 lws_plat_init(struct lws_context *context,
              struct lws_context_creation_info *info)
@@ -565,5 +710,10 @@ lws_plat_init(struct lws_context *context,
        context->fops.read      = _lws_plat_file_read;
        context->fops.write     = _lws_plat_file_write;
 
+#ifdef LWS_WITH_PLUGINS
+       if (info->plugins_dir)
+               lws_plat_plugins_init(context, info->plugins_dir);
+#endif
+
        return 0;
 }
index 1d359bd..7a4f17e 100644 (file)
@@ -374,3 +374,37 @@ lws_callback_on_writable_all_protocol(const struct lws_context *context,
 
        return 0;
 }
+
+
+/**
+ * lws_callback_on_writable_all_protocol_vhost() - Request a callback for
+ *                     all connections using the given protocol when it
+ *                     becomes possible to write to each socket without
+ *                     blocking in turn.
+ *
+ * @vhost:     Only consider connections on this lws_vhost
+ * @protocol:  Protocol whose connections will get callbacks
+ */
+
+LWS_VISIBLE int
+lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost,
+                                     const struct lws_protocols *protocol)
+{
+       const struct lws_context *context = vhost->context;
+       const struct lws_context_per_thread *pt = &context->pt[0];
+       unsigned int n, m = context->count_threads;
+       struct lws *wsi;
+
+       while (m--) {
+               for (n = 0; n < pt->fds_count; n++) {
+                       wsi = wsi_from_fd(context, pt->fds[n].fd);
+                       if (!wsi)
+                               continue;
+                       if (wsi->vhost == vhost && wsi->protocol == protocol)
+                               lws_callback_on_writable(wsi);
+               }
+               pt++;
+       }
+
+       return 0;
+}
index 5ebb974..839eab5 100644 (file)
@@ -261,12 +261,6 @@ typedef unsigned __int64 u_int64_t;
 #endif
 #endif
 
-#include <stddef.h>
-
-#ifndef container_of
-#define container_of(P,T,M)    ((T *)((char *)(P) - offsetof(T, M)))
-#endif
-
 #else
 
 #include <sys/stat.h>
@@ -283,11 +277,6 @@ typedef unsigned __int64 u_int64_t;
 #ifdef __cplusplus
 extern "C" {
 #endif
-#include <stddef.h>
-
-#ifndef container_of
-#define container_of(P,T,M)    ((T *)((char *)(P) - offsetof(T, M)))
-#endif
 
 #if defined(__QNX__)
        #include <gulliver.h>
@@ -664,6 +653,7 @@ struct lws_vhost {
        const char *name;
        const char *iface;
        const struct lws_protocols *protocols;
+       void **protocol_vh_privs;
 #ifdef LWS_OPENSSL_SUPPORT
        SSL_CTX *ssl_ctx;
        SSL_CTX *ssl_client_ctx;
@@ -704,6 +694,7 @@ struct lws_context {
        struct lws **lws_lookup;  /* fd to wsi */
 #endif
        struct lws_vhost *vhost_list;
+       struct lws_plugin *plugin_list;
        const struct lws_token_limits *token_limits;
        void *user_space;
 
@@ -755,9 +746,12 @@ struct lws_context {
        short max_http_header_data;
        short max_http_header_pool;
        short count_threads;
+       short plugin_protocol_count;
+       short plugin_extension_count;
 
        unsigned int being_destroyed:1;
        unsigned int requested_kill:1;
+       unsigned int protocol_init_done:1;
 };
 
 #define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x]
@@ -1671,6 +1665,9 @@ lws_get_addresses(struct lws_context *context, void *ads, char *name,
 LWS_EXTERN int
 lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
 
+int
+lws_protocol_init(struct lws_context *context);
+
 /*
  * custom allocator
  */
index 51dd427..3103256 100644 (file)
@@ -194,6 +194,9 @@ static const char * get_mimetype(const char *file)
        if (!strcmp(&file[n - 4], ".png"))
                return "image/png";
 
+       if (!strcmp(&file[n - 4], ".jpg"))
+               return "image/jpeg";
+
        if (!strcmp(&file[n - 5], ".html"))
                return "text/html";
 
@@ -220,7 +223,7 @@ int lws_http_serve(struct lws *wsi, char *uri, const char *origin)
        }
 
        n = lws_serve_http_file(wsi, path, mimetype, NULL, 0);
-       if (n < 0)
+
        if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
                return -1; /* error or can't reuse connection: close the socket */
 
index 80e513c..2e66812 100644 (file)
@@ -637,6 +637,9 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
        int n = 0, m;
        int more;
 
+       if (!context->protocol_init_done)
+               lws_protocol_init(context);
+
        /*
         * you can call us with pollfd = NULL to just allow the once-per-second
         * global timeout checks; if less than a second since the last check
index f90abeb..452e969 100644 (file)
@@ -17,6 +17,8 @@
 #cmakedefine LWS_USE_MBEDTLS
 #cmakedefine LWS_USE_POLARSSL
 
+#cmakedefine LWS_WITH_PLUGINS
+
 /* The Libwebsocket version */
 #cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}"
 
diff --git a/lwsws/etc-lwsws-conf-EXAMPLE b/lwsws/etc-lwsws-conf-EXAMPLE
new file mode 100644 (file)
index 0000000..ac2c9ac
--- /dev/null
@@ -0,0 +1,14 @@
+# these are the server global settings
+# stuff related to vhosts should go in one
+# file per vhost in ../conf.d/
+
+{
+  "global": {
+   "uid": "99",
+   "gid": "99",
+   "interface": "eth0",
+   "count-threads": "1",
+   "init-ssl": "yes"
+ }
+}
+
diff --git a/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE b/lwsws/etc-lwsws-conf.d-libwebsockets.org-EXAMPLE
new file mode 100644 (file)
index 0000000..b4e307d
--- /dev/null
@@ -0,0 +1,50 @@
+# comment
+
+{
+ "vhosts": [ {
+     "name": "libwebsockets.org",
+     "port": "443",
+     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
+     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
+     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": "file:///var/www/libwebsockets.org",
+       "default": "index.html"
+       }, {
+        "mountpoint": "/git",
+        "origin": "http://git.warmcat.com",
+        "default": "/" 
+       }, {
+        "mountpoint": "/mailman",
+        "origin": "cgi://usr/lib/mailman/cgi-bin/",
+        "default": "/list-info"
+    }]
+   },
+    {
+    "name": "libwebsockets.org", # disambiguated by port, must be same for SNI
+    "port": "7681",
+     "host-ssl-key":  "/etc/pki/tls/private/libwebsockets.org.key",
+     "host-ssl-cert": "/etc/pki/tls/certs/libwebsockets.org.crt",
+     "host-ssl-ca":   "/etc/pki/tls/certs/libwebsockets.org.cer",
+     "ws-protocols": [{
+       "wsprotocol": "dumb-increment-protocol",
+       "wsprotocol": "lws-mirror-protocol",
+       "wsprotocol": "lws-status"
+     }],
+     "ws-extensions": [{
+       "extension": "permessage-deflate"
+     }],
+     "mounts": [{
+       "mountpoint": "/",
+       "origin": "file:///usr/local/share/libwebsockets-test-server",
+       "default": "test.html"
+     }]
+   },
+    {
+    "name": "libwebsockets.org",
+    "port": "80",
+    "global-redirect": "https://libwebsockets.org"
+ }]
+}
+
index 1b27a7d..66f398f 100644 (file)
@@ -47,6 +47,9 @@ const char * get_mimetype(const char *file)
        if (!strcmp(&file[n - 4], ".png"))
                return "image/png";
 
+       if (!strcmp(&file[n - 4], ".jpg"))
+               return "image/jpeg";
+
        if (!strcmp(&file[n - 5], ".html"))
                return "text/html";
 
@@ -190,7 +193,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
                        return 0;
 
                /* check for the "send a big file by hand" example case */
-
+               lwsl_notice("%s\n", in);
                if (!strcmp((const char *)in, "/leaf.jpg")) {
                        if (strlen(resource_path) > sizeof(leaf_path) - 10)
                                return -1;
index 741fe7a..24fc31d 100644 (file)
@@ -62,6 +62,7 @@ static struct lws_protocols protocols[] = {
                sizeof (struct per_session_data__http), /* per_session_data_size */
                0,                      /* max frame size / rx buffer */
        },
+       { }
 };
 
 void sighandler(int sig)
@@ -177,17 +178,16 @@ int main(int argc, char **argv)
 
        info.max_http_header_pool = 16;
        info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 |
-               LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
-#ifdef LWS_USE_LIBUV
-       info.options |= LWS_SERVER_OPTION_LIBUV;
-#endif
+                             LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
+                             LWS_SERVER_OPTION_LIBUV;
+
+       info.plugins_dir = INSTALL_DATADIR"/libwebsockets-test-server/plugins/";
 
        lwsl_notice("Using config dir: \"%s\"\n", config_dir);
 
        /*
         *  first go through the config for creating the outer context
         */
-
        if (lwsws_get_config_globals(&info, config_dir, &cs, &cs_len))
                goto bail;
 
@@ -214,21 +214,14 @@ int main(int argc, char **argv)
        if (lwsws_get_config_vhosts(context, &info, config_dir, &cs, &cs_len))
                goto bail;
 
-#ifdef LWS_USE_LIBUV
        lws_uv_sigint_cfg(context, 1, signal_cb);
        lws_uv_initloop(context, NULL, 0);
-       lws_libuv_run(context, 0);
-#else
 
-       n = 0;
-       while (n >= 0 && !force_exit) {
-               n = lws_service(context, 50);
-       }
-#endif
+       lws_libuv_run(context, 0);
 
 bail:
        lws_context_destroy(context);
-       lwsl_notice("lwsws exited cleanly\n");
+       fprintf(stderr, "lwsws exited cleanly\n");
 
 #ifndef _WIN32
        closelog();
diff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c
new file mode 100644 (file)
index 0000000..499073d
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * ws protocol handler plugin for "dumb increment"
+ *
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
+ *
+ * These test plugins are intended to be adapted for use in your code, which
+ * may be proprietary.  So unlike the library itself, they are licensed
+ * Public Domain.
+ */
+#include "../lib/libwebsockets.h"
+
+struct per_vhost_data__dumb_increment {
+       uv_timer_t timeout_watcher;
+       struct lws_context *context;
+       struct lws_vhost *vhost;
+       const struct lws_protocols *protocol;
+};
+
+struct per_session_data__dumb_increment {
+       int number;
+};
+
+static void
+uv_timeout_cb_dumb_increment(uv_timer_t *w
+#if UV_VERSION_MAJOR == 0
+               , int status
+#endif
+)
+{
+       struct per_vhost_data__dumb_increment *vhd = lws_container_of(w,
+                       struct per_vhost_data__dumb_increment, timeout_watcher);
+       lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
+}
+
+static int
+callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
+                       void *user, void *in, size_t len)
+{
+       struct per_session_data__dumb_increment *pss =
+                       (struct per_session_data__dumb_increment *)user;
+       struct per_vhost_data__dumb_increment *vhd =
+                       (struct per_vhost_data__dumb_increment *)
+                       lws_protocol_vh_priv_get(lws_vhost_get(wsi),
+                                       lws_protocol_get(wsi));
+       unsigned char buf[LWS_PRE + 512];
+       unsigned char *p = &buf[LWS_PRE];
+       int n, m;
+
+       switch (reason) {
+       case LWS_CALLBACK_PROTOCOL_INIT:
+               vhd = lws_protocol_vh_priv_zalloc(lws_vhost_get(wsi),
+                               lws_protocol_get(wsi),
+                               sizeof(struct per_vhost_data__dumb_increment));
+               vhd->context = lws_get_context(wsi);
+               vhd->protocol = lws_protocol_get(wsi);
+               vhd->vhost = lws_vhost_get(wsi);
+               uv_timer_init(lws_uv_getloop(vhd->context, 0),
+                             &vhd->timeout_watcher);
+               uv_timer_start(&vhd->timeout_watcher,
+                              uv_timeout_cb_dumb_increment, 50, 50);
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_DESTROY:
+               uv_timer_stop(&vhd->timeout_watcher);
+               break;
+
+       case LWS_CALLBACK_ESTABLISHED:
+               pss->number = 0;
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+               n = sprintf((char *)p, "%d", pss->number++);
+               m = lws_write(wsi, p, n, LWS_WRITE_TEXT);
+               if (m < n) {
+                       lwsl_err("ERROR %d writing to di socket\n", n);
+                       return -1;
+               }
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               if (len < 6)
+                       break;
+               if (strcmp((const char *)in, "reset\n") == 0)
+                       pss->number = 0;
+               if (strcmp((const char *)in, "closeme\n") == 0) {
+                       lwsl_notice("dumb_inc: closing as requested\n");
+                       lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
+                                        (unsigned char *)"seeya", 5);
+                       return -1;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct lws_protocols protocols[] = {
+       {
+               "dumb-increment-protocol",
+               callback_dumb_increment,
+               sizeof(struct per_session_data__dumb_increment),
+               10, /* rx buf size must be >= permessage-deflate rx size */
+       },
+};
+
+LWS_VISIBLE int
+init_protocol_dumb_increment(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_VISIBLE int
+destroy_protocol_dumb_increment(struct lws_context *context)
+{
+       return 0;
+}
diff --git a/plugins/protocol_lws_mirror.c b/plugins/protocol_lws_mirror.c
new file mode 100644 (file)
index 0000000..ba65660
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
+ *
+ * The test apps are intended to be adapted for use in your code, which
+ * may be proprietary.  So unlike the library itself, they are licensed
+ * Public Domain.
+ */
+#include "../lib/libwebsockets.h"
+
+/* lws-mirror_protocol */
+
+#define MAX_MESSAGE_QUEUE 512
+
+struct per_session_data__lws_mirror {
+       struct lws *wsi;
+       int ringbuffer_tail;
+};
+
+struct a_message {
+       void *payload;
+       size_t len;
+};
+
+struct per_vhost_data__lws_mirror {
+       struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
+       int ringbuffer_head;
+};
+
+static int
+callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
+                   void *user, void *in, size_t len)
+{
+       struct per_session_data__lws_mirror *pss =
+                       (struct per_session_data__lws_mirror *)user;
+       struct per_vhost_data__lws_mirror *v =
+                       (struct per_vhost_data__lws_mirror *)
+                       lws_protocol_vh_priv_get(lws_vhost_get(wsi),
+                                       lws_protocol_get(wsi));
+       int n, m;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_ESTABLISHED:
+               lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
+               pss->ringbuffer_tail = v->ringbuffer_head;
+               pss->wsi = wsi;
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
+               lws_protocol_vh_priv_zalloc(lws_vhost_get(wsi),
+                               lws_protocol_get(wsi),
+                               sizeof(struct per_vhost_data__lws_mirror));
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */
+               lwsl_info("%s: mirror protocol cleaning up %p\n", __func__, v);
+               for (n = 0; n < ARRAY_SIZE(v->ringbuffer); n++)
+                       if (v->ringbuffer[n].payload) {
+                               free(v->ringbuffer[n].payload);
+                               v->ringbuffer[n].payload = NULL;
+                       }
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+               while (pss->ringbuffer_tail != v->ringbuffer_head) {
+                       m = v->ringbuffer[pss->ringbuffer_tail].len;
+                       n = lws_write(wsi, (unsigned char *)
+                                  v->ringbuffer[pss->ringbuffer_tail].payload +
+                                  LWS_PRE, m, LWS_WRITE_TEXT);
+                       if (n < 0) {
+                               lwsl_err("ERROR %d writing to mirror socket\n", n);
+                               return -1;
+                       }
+                       if (n < m)
+                               lwsl_err("mirror partial write %d vs %d\n", n, m);
+
+                       if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
+                               pss->ringbuffer_tail = 0;
+                       else
+                               pss->ringbuffer_tail++;
+
+                       if (((v->ringbuffer_head - pss->ringbuffer_tail) &
+                           (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
+                               lws_rx_flow_allow_all_protocol(lws_get_context(wsi),
+                                              lws_get_protocol(wsi));
+
+                       if (lws_send_pipe_choked(wsi)) {
+                               lws_callback_on_writable(wsi);
+                               break;
+                       }
+               }
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               if (((v->ringbuffer_head - pss->ringbuffer_tail) &
+                   (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
+                       lwsl_err("dropping!\n");
+                       goto choke;
+               }
+
+               if (v->ringbuffer[v->ringbuffer_head].payload)
+                       free(v->ringbuffer[v->ringbuffer_head].payload);
+
+               v->ringbuffer[v->ringbuffer_head].payload = malloc(LWS_PRE + len);
+               v->ringbuffer[v->ringbuffer_head].len = len;
+               memcpy((char *)v->ringbuffer[v->ringbuffer_head].payload +
+                      LWS_PRE, in, len);
+               if (v->ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
+                       v->ringbuffer_head = 0;
+               else
+                       v->ringbuffer_head++;
+
+               if (((v->ringbuffer_head - pss->ringbuffer_tail) &
+                   (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
+                       goto done;
+
+choke:
+               lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
+               lws_rx_flow_control(wsi, 0);
+
+done:
+               lws_callback_on_writable_all_protocol(lws_get_context(wsi),
+                                                     lws_get_protocol(wsi));
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct lws_protocols protocols[] = {
+       {
+               "lws-mirror-protocol",
+               callback_lws_mirror,
+               sizeof(struct per_session_data__lws_mirror),
+               128, /* rx buf size must be >= permessage-deflate rx size */
+       },
+};
+
+LWS_VISIBLE int
+init_protocol_lws_mirror(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_VISIBLE int
+destroy_protocol_lws_mirror(struct lws_context *context)
+{
+       return 0;
+}
+
diff --git a/plugins/protocol_lws_status.c b/plugins/protocol_lws_status.c
new file mode 100644 (file)
index 0000000..fabaf6d
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
+ *
+ * The test apps are intended to be adapted for use in your code, which
+ * may be proprietary.  So unlike the library itself, they are licensed
+ * Public Domain.
+ */
+#include "../lib/libwebsockets.h"
+#include <time.h>
+
+struct per_session_data__lws_status {
+       struct per_session_data__lws_status *list;
+       struct timeval tv_established;
+       int last;
+       char ip[270];
+       char user_agent[512];
+       const char *pos;
+       int len;
+};
+
+
+static unsigned char server_info[1024];
+static int server_info_len;
+static int current;
+static char cache[16384];
+static int cache_len;
+static struct per_session_data__lws_status *list;
+static int live_wsi;
+
+
+static void
+update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
+{
+       struct per_session_data__lws_status **pp = &list;
+       int subsequent = 0;
+       char *p = cache + LWS_PRE, *start = p;
+       char date[128];
+       time_t t;
+       struct tm *ptm;
+#ifndef WIN32
+       struct tm tm;
+#endif
+
+       p += snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
+                    server_info, live_wsi);
+
+       /* render the list */
+       while (*pp) {
+               t = (*pp)->tv_established.tv_sec;
+#ifdef WIN32
+               ptm = localtime(&t);
+               if (!ptm)
+#else
+               ptm = &tm;
+               if (!localtime_r(&t, &tm))
+#endif
+                       strcpy(date, "unknown");
+               else
+                       strftime(date, sizeof(date), "%F %H:%M %Z", ptm);
+               if ((p - start) > (sizeof(cache) - 512))
+                       break;
+               if (subsequent)
+                       *p++ = ',';
+               subsequent = 1;
+               p += snprintf(p, sizeof(cache) - (p - start) - 1,
+                               "{\"peer\":\"%s\",\"time\":\"%s\","
+                               "\"ua\":\"%s\"}",
+                            (*pp)->ip, date, (*pp)->user_agent);
+               pp = &((*pp)->list);
+       }
+
+       p += sprintf(p, "]}");
+       cache_len = p - start;
+       lwsl_err("cache_len %d\n", cache_len);
+       *p = '\0';
+
+       /* since we changed the list, increment the 'version' */
+       current++;
+       /* update everyone */
+       lws_callback_on_writable_all_protocol(lws_get_context(wsi),
+                                             lws_get_protocol(wsi));
+}
+
+
+/* lws-status protocol */
+
+int
+callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
+                   void *user, void *in, size_t len)
+{
+       struct per_session_data__lws_status *pss =
+                       (struct per_session_data__lws_status *)user,
+                       **pp;
+       char name[128], rip[128];
+       int m;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_PROTOCOL_INIT:
+               /*
+                * Prepare the static server info
+                */
+               server_info_len = sprintf((char *)server_info,
+                                         "\"version\":\"%s\","
+                                         " \"hostname\":\"%s\"",
+                                         lws_get_library_version(),
+                                         lws_canonical_hostname(
+                                                       lws_get_context(wsi)));
+               break;
+
+       case LWS_CALLBACK_ESTABLISHED:
+               /*
+                * we keep a linked list of live pss, so we can walk it
+                */
+               pss->last = 0;
+               pss->list = list;
+               list = pss;
+               live_wsi++;
+               lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name,
+                                      sizeof(name), rip, sizeof(rip));
+               sprintf(pss->ip, "%s (%s)", name, rip);
+               gettimeofday(&pss->tv_established, NULL);
+               strcpy(pss->user_agent, "unknown");
+               lws_hdr_copy(wsi, pss->user_agent, sizeof(pss->user_agent),
+                            WSI_TOKEN_HTTP_USER_AGENT);
+               update_status(wsi, pss);
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+               m = lws_write(wsi, (unsigned char *)cache + LWS_PRE, cache_len,
+                             LWS_WRITE_TEXT);
+               if (m < server_info_len) {
+                       lwsl_err("ERROR %d writing to di socket\n", m);
+                       return -1;
+               }
+               break;
+
+       case LWS_CALLBACK_CLOSED:
+               /*
+                * remove ourselves from live pss list
+                */
+               lwsl_err("CLOSING pss %p ********\n", pss);
+
+               pp = &list;
+               while (*pp) {
+                       if (*pp == pss) {
+                               *pp = pss->list;
+                               pss->list = NULL;
+                               live_wsi--;
+                               break;
+                       }
+                       pp = &((*pp)->list);
+               }
+
+               update_status(wsi, pss);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct lws_protocols protocols[] = {
+       {
+               "lws-status",
+               callback_lws_status,
+               sizeof(struct per_session_data__lws_status),
+               128, /* rx buf size must be >= permessage-deflate rx size */
+       },
+};
+
+LWS_VISIBLE int
+init_protocol_lws_status(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_VISIBLE int
+destroy_protocol_lws_status(struct lws_context *context)
+{
+       return 0;
+}
index fb95f7f..183c2b7 100644 (file)
@@ -128,7 +128,11 @@ void signal_cb(uv_signal_t *watcher, int signum)
 }
 
 static void
-uv_timeout_cb_dumb_increment(uv_timer_t *w)
+uv_timeout_cb_dumb_increment(uv_timer_t *w
+#if UV_VERSION_MAJOR == 0
+               , int status
+#endif
+)
 {
        lws_callback_on_writable_all_protocol(context,
                                        &protocols[PROTOCOL_DUMB_INCREMENT]);
index 96ac72d..f51f087 100755 (executable)
@@ -10,6 +10,12 @@ then
        then
                sudo apt-get install -y -qq libev-dev;
        fi
+
+       if [ "$LWS_METHOD" == "libuv" -o "$LWS_METHOD" == "lwsws" ];
+       then
+               sudo apt-get install -y -qq libuv-dev;
+       fi
+
 fi
 
 if [ "$TRAVIS_OS_NAME" == "osx" ];
@@ -18,6 +24,12 @@ then
        then
                brew install libev;
        fi
+
+       if [ "$LWS_METHOD" == "libuv" -o "$LWS_METHOD" == "lwsws" ];
+       then
+               brew install libuv;
+       fi
+
 fi