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"
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
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)
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)
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()
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)
#
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.
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"
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()
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
-------------
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
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.
+
+
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,
{
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;
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;
}
#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;
#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;
{
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;
/*
* 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;
}
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);
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;
}
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__);
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;
}
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__);
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;
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);
}
}
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;
}
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
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
/**
#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]))
* 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
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
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);
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);
#include <pwd.h>
#include <grp.h>
+#include <dlfcn.h>
+#include <dirent.h>
+
+
/*
* included from libwebsockets.c for unix builds
*/
}
}
+#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)
{
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);
return 0;
}
+
LWS_VISIBLE int
lws_plat_init(struct lws_context *context,
struct lws_context_creation_info *info)
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;
}
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;
+}
#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>
#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>
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;
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;
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]
LWS_EXTERN int
lws_cgi_kill_terminated(struct lws_context_per_thread *pt);
+int
+lws_protocol_init(struct lws_context *context);
+
/*
* custom allocator
*/
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";
}
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 */
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
#cmakedefine LWS_USE_MBEDTLS
#cmakedefine LWS_USE_POLARSSL
+#cmakedefine LWS_WITH_PLUGINS
+
/* The Libwebsocket version */
#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}"
--- /dev/null
+# 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"
+ }
+}
+
--- /dev/null
+# 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"
+ }]
+}
+
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";
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;
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
},
+ { }
};
void sighandler(int sig)
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;
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();
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
}
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]);
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" ];
then
brew install libev;
fi
+
+ if [ "$LWS_METHOD" == "libuv" -o "$LWS_METHOD" == "lwsws" ];
+ then
+ brew install libuv;
+ fi
+
fi