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>
This commit is contained in:
Andy Green 2016-04-06 16:15:40 +08:00
parent 09998e3ad8
commit 020770566e
23 changed files with 1247 additions and 71 deletions

View 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

View 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()

View 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.
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.

View 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);

View 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;

View 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;
}

View 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
/**

View 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);

View 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;
}

View 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;
}

View 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
*/

View 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 */

View 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

View 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}"

View file

@ -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"
}
}

View file

@ -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"
}]
}

View 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;

View 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();

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View 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]);

View file

@ -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