diff --git a/CMakeLists.txt b/CMakeLists.txt index f1911176f..791ca0af1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ option(LWS_USE_CYASSL "Use CyaSSL replacement for OpenSSL. When setting this, yo option(LWS_USE_WOLFSSL "Use wolfSSL replacement for OpenSSL. When setting this, you also need to specify LWS_WOLFSSL_LIBRARIES and LWS_WOLFSSL_INCLUDE_DIRS" OFF) option(LWS_WITH_ZLIB "Include zlib support (required for extensions)" ON) option(LWS_WITH_LIBEV "Compile with support for libev" OFF) +option(LWS_WITH_LIBUV "Compile with support for libuv" OFF) option(LWS_USE_BUNDLED_ZLIB "Use bundled zlib version (Windows only)" ${LWS_USE_BUNDLED_ZLIB_DEFAULT}) option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of the OS-installed CA root certs" ON) option(LWS_WITHOUT_BUILTIN_GETIFADDRS "Don't use the BSD getifaddrs implementation from libwebsockets if it is missing (this will result in a compilation error) ... The default is to assume that your libc provides it. On some systems such as uclibc it doesn't exist." OFF) @@ -135,6 +136,8 @@ set(LWS_WOLFSSL_LIBRARIES CACHE PATH "Path to the wolfSSL library") set(LWS_WOLFSSL_INCLUDE_DIRS CACHE PATH "Path to the wolfSSL include directory") set(LWS_LIBEV_LIBRARIES CACHE PATH "Path to the libev library") set(LWS_LIBEV_INCLUDE_DIRS CACHE PATH "Path to the libev include directory") +set(LWS_LIBUV_LIBRARIES CACHE PATH "Path to the libuv library") +set(LWS_LIBUV_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory") if (NOT LWS_WITH_SSL) set(LWS_WITHOUT_BUILTIN_SHA1 OFF) @@ -187,6 +190,15 @@ if (LWS_WITH_LIBEV) endif() endif() +if (LWS_WITH_LIBUV) + if ("${LWS_LIBUV_LIBRARIES}" STREQUAL "" OR "${LWS_LIBUV_INCLUDE_DIRS}" STREQUAL "") + else() + set(LIBUV_LIBRARIES ${LWS_LIBUV_LIBRARIES}) + set(LIBUV_INCLUDE_DIRS ${LWS_LIBUV_INCLUDE_DIRS}) + set(LIBUV_FOUND 1) + endif() +endif() + # FIXME: This must be runtime-only option. # The base dir where the test-apps look for the SSL certs. set(LWS_OPENSSL_CLIENT_CERTS ../share CACHE PATH "Server SSL certificate directory") @@ -233,6 +245,10 @@ if (LWS_WITH_LIBEV) set(LWS_USE_LIBEV 1) endif() +if (LWS_WITH_LIBUV) + set(LWS_USE_LIBUV 1) +endif() + if (LWS_IPV6) set(LWS_USE_IPV6 1) endif() @@ -461,7 +477,12 @@ endif() if (LWS_WITH_LIBEV) list(APPEND SOURCES lib/libev.c) -endif(LWS_WITH_LIBEV) +endif() + +if (LWS_WITH_LIBUV) + list(APPEND SOURCES + lib/libuv.c) +endif() # Add helper files for Windows. if (WIN32) @@ -676,6 +697,20 @@ if (LWS_WITH_LIBEV) list(APPEND LIB_LIST ${LIBEV_LIBRARIES}) endif(LWS_WITH_LIBEV) +if (LWS_WITH_LIBUV) + if (NOT LIBUV_FOUND) + find_path(LIBUV_INCLUDE_DIRS NAMES uv.h) + find_library(LIBUV_LIBRARIES NAMES uv) + if(LIBUV_INCLUDE_DIRS AND LIBUV_LIBRARIES) + set(LIBUV_FOUND 1) + endif() + endif() + message("libuv include dir: ${LIBUV_INCLUDE_DIRS}") + message("libuv libraries: ${LIBUV_LIBRARIES}") + include_directories("${LIBUV_INCLUDE_DIRS}") + list(APPEND LIB_LIST ${LIBUV_LIBRARIES}) +endif() + # # Platform specific libs. # @@ -808,6 +843,15 @@ if (NOT LWS_WITHOUT_TESTAPPS) "test-server/test-server-mirror.c" "test-server/test-server-echogen.c") endif() + if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + AND LWS_WITH_LIBUV) + create_test_app(test-server-libuv + "test-server/test-server-libuv.c" + "test-server/test-server-http.c" + "test-server/test-server-dumb-increment.c" + "test-server/test-server-mirror.c" + "test-server/test-server-echogen.c") + endif() endif() # @@ -1134,6 +1178,7 @@ message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}") message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}") message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}") message(" LWS_USE_LIBEV = ${LWS_USE_LIBEV}") +message(" LWS_USE_LIBUV = ${LWS_USE_LIBUV}") message(" LWS_IPV6 = ${LWS_IPV6}") message(" LWS_WITH_HTTP2 = ${LWS_WITH_HTTP2}") message(" LWS_MBED3 = ${LWS_MBED3}") diff --git a/README.coding.md b/README.coding.md index d425bacc2..72a124083 100644 --- a/README.coding.md +++ b/README.coding.md @@ -378,3 +378,19 @@ libwebsockets-test-server-pthread does it, for the FD locking callbacks. There is no knowledge or dependency in lws itself about pthreads. How the locking is implemented is entirely up to the user code. + +Libev / Libuv support +--------------------- + +You can select either or both + +-DLWS_WITH_LIBEV=1 +-DLWS_WITH_LIBUV=1 + +at cmake configure-time. The user application may use one of the +context init options flags + +LWS_SERVER_OPTION_LIBEV +LWS_SERVER_OPTION_LIBUV + +to indicate it will use either of the event libraries. diff --git a/changelog b/changelog index 581101084..029001873 100644 --- a/changelog +++ b/changelog @@ -222,6 +222,24 @@ had just been accepted by lws' own listen socket. 9) X-Real-IP: header has been added as WSI_TOKEN_HTTP_X_REAL_IP +10) Libuv support is added, there are new related user apis + +typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int revents); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + lws_uv_signal_cb_t *cb); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + +LWS_VISIBLE void +lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents); + +and CMAKE option + +LWS_WITH_LIBUV + User api changes ---------------- @@ -262,6 +280,21 @@ the library. of value 73. That's now corrected and WSI_TOKEN_PROXY moved to his own place at 77. +9) With the addition of libuv support, libev is not the only event loop +library in town and his api names must be elaborated with _ev_ + + Callback typedef: lws_signal_cb ---> lws_ev_signal_cb_t + lws_sigint_cfg --> lws_ev_sigint_cfg + lws_initloop --> lws_ev_initloop + lws_sigint_cb --> lws_ev_sigint_cb + +10) Libev support is made compatible with multithreaded service, +lws_ev_initloop (was lws_initloop) gets an extra argument for the +thread service index (use 0 if you will just have 1 service thread). + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + v1.6.0-chrome48-firefox42 ======================= diff --git a/lib/client-handshake.c b/lib/client-handshake.c index 76b2fbdd1..83bcef297 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -233,7 +233,9 @@ lws_client_connect_2(struct lws *wsi) */ if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) goto failed; + lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE); return wsi; } diff --git a/lib/client.c b/lib/client.c index 849da5a0d..ad15ed0a3 100644 --- a/lib/client.c +++ b/lib/client.c @@ -144,7 +144,9 @@ lws_client_socket_service(struct lws_context *context, struct lws *wsi, */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) return -1; + lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); #ifdef LWS_OPENSSL_SUPPORT /* we can retry this... just cook the SSL BIO the first time */ diff --git a/lib/context.c b/lib/context.c index 20650cb5b..da653e3d4 100644 --- a/lib/context.c +++ b/lib/context.c @@ -199,8 +199,18 @@ lws_create_context(struct lws_context_creation_info *info) * before invoking lws_initloop: */ context->use_ev_sigint = 1; - context->lws_ev_sigint_cb = &lws_sigint_cb; + context->lws_ev_sigint_cb = &lws_ev_sigint_cb; #endif /* LWS_USE_LIBEV */ +#ifdef LWS_USE_LIBUV + /* (Issue #264) In order to *avoid breaking backwards compatibility*, we + * enable libev mediated SIGINT handling with a default handler of + * lws_sigint_cb. The handler can be overridden or disabled + * by invoking lws_sigint_cfg after creating the context, but + * before invoking lws_initloop: + */ + context->use_ev_sigint = 1; + context->lws_uv_sigint_cb = &lws_uv_sigint_cb; +#endif lwsl_info(" mem: context: %5u bytes (%d ctx + (%d thr x %d))\n", sizeof(struct lws_context) + @@ -377,14 +387,18 @@ lws_context_destroy(struct lws_context *context) protocol++; } } -#ifdef LWS_USE_LIBEV - uv_poll_stop(&context->w_accept.watcher); - //ev_io_stop(context->io_loop, &context->w_accept.watcher); - //if (context->use_ev_sigint) - //ev_signal_stop(context->io_loop, &context->w_sigint.watcher); -#endif /* LWS_USE_LIBEV */ for (n = 0; n < context->count_threads; n++) { +#ifdef LWS_USE_LIBEV + ev_io_stop(context->pt[n].io_loop_ev, + &context->pt[n].w_accept.ev_watcher); + if (context->use_ev_sigint) + ev_signal_stop(context->pt[n].io_loop_ev, + &context->pt[n].w_sigint.ev_watcher); +#endif /* LWS_USE_LIBEV */ +#ifdef LWS_USE_LIBUV + uv_poll_stop(&context->pt[n].w_accept.uv_watcher); +#endif lws_free_set_NULL(context->pt[n].serv_buf); if (context->pt[n].ah_pool) lws_free(context->pt[n].ah_pool); diff --git a/lib/libev.c b/lib/libev.c index a7fd1cb8e..179c01092 100644 --- a/lib/libev.c +++ b/lib/libev.c @@ -30,24 +30,24 @@ void lws_feature_status_libev(struct lws_context_creation_info *info) } static void -lws_accept_cb(uv_poll_t *watcher, int status, int revents) +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, watcher); + struct lws_io_watcher, ev_watcher); struct lws_context *context = lws_io->context; struct lws_pollfd eventfd; - if (status < 0/*revents & EV_ERROR*/) - return; + if (revents & EV_ERROR) + return; - eventfd.fd = watcher->io_watcher.fd; + eventfd.fd = watcher->fd; eventfd.events = 0; - eventfd.revents = 0;//EV_NONE; - if (revents & UV_READABLE) { + eventfd.revents = EV_NONE; + if (revents & EV_READ) { eventfd.events |= LWS_POLLIN; eventfd.revents |= LWS_POLLIN; } - if (revents & UV_WRITABLE) { + if (revents & EV_WRITE) { eventfd.events |= LWS_POLLOUT; eventfd.revents |= LWS_POLLOUT; } @@ -55,54 +55,51 @@ lws_accept_cb(uv_poll_t *watcher, int status, int revents) } LWS_VISIBLE void -lws_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents) +lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents) { - //ev_break(loop, EVBREAK_ALL); + ev_break(loop, EVBREAK_ALL); } LWS_VISIBLE int -lws_sigint_cfg(struct lws_context *context, int use_ev_sigint, - lws_ev_signal_cb* cb) +lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, + lws_ev_signal_cb_t *cb) { context->use_ev_sigint = use_ev_sigint; if (cb) context->lws_ev_sigint_cb = cb; else - context->lws_ev_sigint_cb = &lws_sigint_cb; + context->lws_ev_sigint_cb = &lws_ev_sigint_cb; return 0; } LWS_VISIBLE int -lws_initloop(struct lws_context *context, uv_loop_t *loop) +lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi) { - //uv_signal_t *w_sigint = &context->w_sigint.watcher; - uv_poll_t *w_accept = &context->w_accept.watcher; - //const char * backend_name; + struct ev_signal *w_sigint = &context->pt[tsi].w_sigint.ev_watcher; + struct ev_io *w_accept = &context->pt[tsi].w_accept.ev_watcher; + const char * backend_name; int status = 0; - //int backend; - int m = 0; /* !!! TODO add pt support */ + int backend; if (!loop) - loop = uv_default_loop(); + loop = ev_loop_new(0); - context->io_loop = loop; + context->pt[tsi].io_loop_ev = loop; /* * Initialize the accept w_accept with the listening socket * and register a callback for read operations */ - uv_poll_init(context->io_loop, w_accept, context->pt[m].lserv_fd); - uv_poll_start(w_accept, UV_READABLE, lws_accept_cb); - //ev_io_init(w_accept, lws_accept_cb, context->pt[m].lserv_fd, UV_READABLE); - //ev_io_start(context->io_loop,w_accept); + ev_io_init(w_accept, lws_accept_cb, context->pt[tsi].lserv_fd, EV_READ); + ev_io_start(context->pt[tsi].io_loop_ev, w_accept); /* Register the signal watcher unless the user says not to */ if (context->use_ev_sigint) { - //ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT); - //ev_signal_start(context->io_loop,w_sigint); + ev_signal_init(w_sigint, context->lws_ev_sigint_cb, SIGINT); + ev_signal_start(context->pt[tsi].io_loop_ev, w_sigint); } - /*backend = ev_backend(loop); + backend = ev_backend(loop); switch (backend) { case EVBACKEND_SELECT: @@ -128,7 +125,7 @@ lws_initloop(struct lws_context *context, uv_loop_t *loop) break; } - lwsl_notice(" libev backend: %s\n", backend_name);*/ + lwsl_notice(" libev backend: %s\n", backend_name); return status; } @@ -137,73 +134,65 @@ LWS_VISIBLE void lws_libev_accept(struct lws *new_wsi, int accept_fd) { struct lws_context *context = lws_get_context(new_wsi); - uv_poll_t *r = &new_wsi->w_read.watcher; - //uv_poll_t *w = &new_wsi->w_write.watcher; + struct ev_io *r = &new_wsi->w_read.ev_watcher; + struct ev_io *w = &new_wsi->w_write.ev_watcher; if (!LWS_LIBEV_ENABLED(context)) return; new_wsi->w_read.context = context; new_wsi->w_write.context = context; - uv_poll_init(context->io_loop, r, accept_fd); - //ev_io_init(r, lws_accept_cb, accept_fd, UV_READABLE); - //ev_io_init(w, lws_accept_cb, accept_fd, UV_WRITABLE); + ev_io_init(r, lws_accept_cb, accept_fd, EV_READ); + ev_io_init(w, lws_accept_cb, accept_fd, EV_WRITE); } LWS_VISIBLE void lws_libev_io(struct lws *wsi, int flags) { struct lws_context *context = lws_get_context(wsi); - int current_events = wsi->w_read.watcher.io_watcher.pevents & (UV_READABLE | UV_WRITABLE); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; if (!LWS_LIBEV_ENABLED(context)) return; - if (!context->io_loop) + if (!pt->io_loop_ev) return; assert((flags & (LWS_EV_START | LWS_EV_STOP)) && (flags & (LWS_EV_READ | LWS_EV_WRITE))); if (flags & LWS_EV_START) { - if (flags & LWS_EV_WRITE) - current_events |= UV_WRITABLE; - //ev_io_start(context->io_loop, &wsi->w_write.watcher); - if (flags & LWS_EV_READ) - current_events |= UV_READABLE; - //ev_io_start(context->io_loop, &wsi->w_read.watcher); - - uv_poll_start(&wsi->w_read.watcher, current_events, lws_accept_cb); + if (flags & LWS_EV_WRITE) + ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher); + if (flags & LWS_EV_READ) + ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher); } else { - if (flags & LWS_EV_WRITE) - current_events &= ~UV_WRITABLE; - //ev_io_stop(context->io_loop, &wsi->w_write.watcher); - if (flags & LWS_EV_READ) - current_events &= ~UV_READABLE; - //ev_io_stop(context->io_loop, &wsi->w_read.watcher); - - if (!(current_events & (UV_READABLE | UV_WRITABLE))) - uv_poll_stop(&wsi->w_read.watcher); - else - uv_poll_start(&wsi->w_read.watcher, current_events, lws_accept_cb); + if (flags & LWS_EV_WRITE) + ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher); + if (flags & LWS_EV_READ) + ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher); } } LWS_VISIBLE int lws_libev_init_fd_table(struct lws_context *context) { + int n; + if (!LWS_LIBEV_ENABLED(context)) return 0; - context->w_accept.context = context; - context->w_sigint.context = context; + for (n = 0; n < context->count_threads; n++) { + context->pt[n].w_accept.context = context; + context->pt[n].w_sigint.context = context; + } return 1; } LWS_VISIBLE void -lws_libev_run(const struct lws_context *context) +lws_libev_run(const struct lws_context *context, int tsi) { - if (context->io_loop && LWS_LIBEV_ENABLED(context)) - uv_run(context->io_loop, 0); + if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context)) + ev_run(context->pt[tsi].io_loop_ev, 0); } diff --git a/lib/libuv.c b/lib/libuv.c new file mode 100644 index 000000000..a7dc3fe35 --- /dev/null +++ b/lib/libuv.c @@ -0,0 +1,176 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2016 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-libwebsockets.h" + +void +lws_feature_status_libuv(struct lws_context_creation_info *info) +{ + if (info->options & LWS_SERVER_OPTION_LIBUV) + lwsl_notice("libuv support compiled in and enabled\n"); + else + lwsl_notice("libuv support compiled in but disabled\n"); +} + +static void +lws_accept_cb(uv_poll_t *watcher, int status, int revents) +{ + struct lws_io_watcher *lws_io = container_of(watcher, + struct lws_io_watcher, uv_watcher); + struct lws_context *context = lws_io->context; + struct lws_pollfd eventfd; + + if (status < 0) + return; + + eventfd.fd = watcher->io_watcher.fd; + eventfd.events = 0; + eventfd.revents = 0;//EV_NONE; + if (revents & UV_READABLE) { + eventfd.events |= LWS_POLLIN; + eventfd.revents |= LWS_POLLIN; + } + if (revents & UV_WRITABLE) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + lws_service_fd(context, &eventfd); +} + +LWS_VISIBLE void +lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents) +{ + //ev_break(loop, EVBREAK_ALL); +} + +LWS_VISIBLE int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + lws_uv_signal_cb_t *cb) +{ + context->use_ev_sigint = use_uv_sigint; + if (cb) + context->lws_uv_sigint_cb = cb; + else + context->lws_uv_sigint_cb = &lws_uv_sigint_cb; + + return 0; +} + +LWS_VISIBLE int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi) +{ + uv_poll_t *w_accept = &context->pt[tsi].w_accept.uv_watcher; + int status = 0; + + if (!loop) + loop = uv_loop_new(); + + context->pt[tsi].io_loop_uv = loop; + + /* + * Initialize the accept w_accept with the listening socket + * and register a callback for read operations + */ + uv_poll_init(context->pt[tsi].io_loop_uv, w_accept, + context->pt[tsi].lserv_fd); + uv_poll_start(w_accept, UV_READABLE, lws_accept_cb); + + return status; +} + +LWS_VISIBLE void +lws_libuv_accept(struct lws *new_wsi, int accept_fd) +{ + struct lws_context *context = lws_get_context(new_wsi); + uv_poll_t *r = &new_wsi->w_read.uv_watcher; + + if (!LWS_LIBUV_ENABLED(context)) + return; + + new_wsi->w_read.context = context; + new_wsi->w_write.context = context; + + uv_poll_init(context->pt[(int)new_wsi->tsi].io_loop_uv, r, accept_fd); +} + +LWS_VISIBLE void +lws_libuv_io(struct lws *wsi, int flags) +{ + struct lws_context *context = lws_get_context(wsi); + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + int current_events = wsi->w_read.uv_watcher.io_watcher.pevents & + (UV_READABLE | UV_WRITABLE); + + if (!LWS_LIBUV_ENABLED(context)) + return; + + if (!pt->io_loop_uv) + return; + + assert((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE))); + + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + current_events |= UV_WRITABLE; + + if (flags & LWS_EV_READ) + current_events |= UV_READABLE; + + uv_poll_start(&wsi->w_read.uv_watcher, current_events, + lws_accept_cb); + } else { + if (flags & LWS_EV_WRITE) + current_events &= ~UV_WRITABLE; + + if (flags & LWS_EV_READ) + current_events &= ~UV_READABLE; + + if (!(current_events & (UV_READABLE | UV_WRITABLE))) + uv_poll_stop(&wsi->w_read.uv_watcher); + else + uv_poll_start(&wsi->w_read.uv_watcher, current_events, + lws_accept_cb); + } +} + +LWS_VISIBLE int +lws_libuv_init_fd_table(struct lws_context *context) +{ + int n; + + if (!LWS_LIBUV_ENABLED(context)) + return 0; + + for (n = 0; n < context->count_threads; n++) { + context->pt[n].w_accept.context = context; + context->pt[n].w_sigint.context = context; + } + + return 1; +} + +LWS_VISIBLE void +lws_libuv_run(const struct lws_context *context, int tsi) +{ + if (context->pt[tsi].io_loop_uv && LWS_LIBUV_ENABLED(context)) + uv_run(context->pt[tsi].io_loop_uv, 0); +} diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 81293cda6..11ef1258d 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -169,8 +169,11 @@ struct sockaddr_in; #endif #ifdef LWS_USE_LIBEV -#include +#include #endif /* LWS_USE_LIBEV */ +#ifdef LWS_USE_LIBUV +#include +#endif /* LWS_USE_LIBUV */ #ifndef LWS_EXTERN #define LWS_EXTERN extern @@ -280,6 +283,7 @@ enum lws_context_options { LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED = (1 << 7), LWS_SERVER_OPTION_VALIDATE_UTF8 = (1 << 8), LWS_SERVER_OPTION_SSL_ECDH = (1 << 9), + LWS_SERVER_OPTION_LIBUV = (1 << 10), /****** add new things just above ---^ ******/ }; @@ -1456,19 +1460,33 @@ LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_http_transaction_completed(struct lws *wsi); #ifdef LWS_USE_LIBEV -typedef void (lws_ev_signal_cb)(uv_loop_t *l, uv_signal_t *w, int revents); +typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents); LWS_VISIBLE LWS_EXTERN int -lws_sigint_cfg(struct lws_context *context, int use_ev_sigint, - lws_ev_signal_cb *cb); +lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, + lws_ev_signal_cb_t *cb); LWS_VISIBLE LWS_EXTERN int -lws_initloop(struct lws_context *context, uv_loop_t *loop); +lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi); LWS_VISIBLE void -lws_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents); +lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); #endif /* LWS_USE_LIBEV */ +#ifdef LWS_USE_LIBUV +typedef void (lws_uv_signal_cb_t)(uv_loop_t *l, uv_signal_t *w, int revents); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, + lws_uv_signal_cb_t *cb); + +LWS_VISIBLE LWS_EXTERN int +lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); + +LWS_VISIBLE void +lws_uv_sigint_cb(uv_loop_t *loop, uv_signal_t *watcher, int revents); +#endif /* LWS_USE_LIBUV */ + LWS_VISIBLE LWS_EXTERN int lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd); diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c index 0963debe4..eb09ce846 100644 --- a/lib/lws-plat-unix.c +++ b/lib/lws-plat-unix.c @@ -134,7 +134,8 @@ lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) if (!context) return 1; - lws_libev_run(context); + lws_libev_run(context, tsi); + lws_libuv_run(context, tsi); if (!context->service_tid_detected) { struct lws _lws; @@ -455,6 +456,8 @@ lws_plat_insert_socket_into_fds(struct lws_context *context, struct lws *wsi) struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); + pt->fds[pt->fds_count++].revents = 0; } @@ -579,7 +582,8 @@ lws_plat_init(struct lws_context *context, return 1; } - if (!lws_libev_init_fd_table(context)) { + if (!lws_libev_init_fd_table(context) && + !lws_libuv_init_fd_table(context)) { /* otherwise libev handled it instead */ while (n--) { diff --git a/lib/pollfd.c b/lib/pollfd.c index 96406fa75..0b0dece1e 100644 --- a/lib/pollfd.c +++ b/lib/pollfd.c @@ -168,6 +168,7 @@ remove_wsi_socket_from_fds(struct lws *wsi) return -1; lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); lws_pt_lock(pt); @@ -315,6 +316,7 @@ network_sock: return -1; lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE); return 1; } diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index d20da12ed..5c5c0287a 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -117,8 +117,11 @@ #include #include #ifdef LWS_USE_LIBEV +#include +#endif +#ifdef LWS_USE_LIBUV #include -#endif /* LWS_USE_LIBEV */ +#endif #include #endif /* MBED */ @@ -437,17 +440,28 @@ enum { struct lws_protocols; struct lws; -#ifdef LWS_USE_LIBEV +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) + struct lws_io_watcher { - uv_poll_t watcher; +#ifdef LWS_USE_LIBEV + ev_io ev_watcher; +#endif +#ifdef LWS_USE_LIBUV + uv_poll_t uv_watcher; +#endif struct lws_context* context; }; struct lws_signal_watcher { - uv_signal_t watcher; +#ifdef LWS_USE_LIBEV + ev_signal ev_watcher; +#endif +#ifdef LWS_USE_LIBUV + uv_signal_t uv_watcher; +#endif struct lws_context* context; }; -#endif /* LWS_USE_LIBEV */ +#endif #ifdef _WIN32 #define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS) @@ -520,6 +534,16 @@ struct lws_context_per_thread { #endif #ifndef LWS_NO_SERVER struct lws *wsi_listening; +#endif +#if defined(LWS_USE_LIBEV) + struct ev_loop *io_loop_ev; +#endif +#if defined(LWS_USE_LIBUV) + uv_loop_t *io_loop_uv; +#endif +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) + struct lws_io_watcher w_accept; + struct lws_signal_watcher w_sigint; #endif lws_sockfd_type lserv_fd; @@ -559,12 +583,6 @@ struct lws_context { #else struct lws **lws_lookup; /* fd to wsi */ #endif -#ifdef LWS_USE_LIBEV - uv_loop_t *io_loop; - struct lws_io_watcher w_accept; - struct lws_signal_watcher w_sigint; - lws_ev_signal_cb* lws_ev_sigint_cb; -#endif /* LWS_USE_LIBEV */ const char *iface; const struct lws_token_limits *token_limits; void *user_space; @@ -578,7 +596,12 @@ struct lws_context { #ifndef LWS_NO_EXTENSIONS const struct lws_extension *extensions; #endif - +#if defined(LWS_USE_LIBEV) + lws_ev_signal_cb_t * lws_ev_sigint_cb; +#endif +#if defined(LWS_USE_LIBUV) + lws_uv_signal_cb_t * lws_uv_sigint_cb; +#endif char http_proxy_address[128]; char proxy_basic_auth_token[128]; char canonical_hostname[128]; @@ -589,7 +612,7 @@ struct lws_context { int max_fds; int listen_port; -#ifdef LWS_USE_LIBEV +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) int use_ev_sigint; #endif int started_with_parent; @@ -643,9 +666,7 @@ enum { LWS_EV_STOP = (1 << 3), }; -#ifdef LWS_USE_LIBEV -#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV) -LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info); +#if defined(LWS_USE_LIBEV) LWS_EXTERN void lws_libev_accept(struct lws *new_wsi, lws_sockfd_type accept_fd); LWS_EXTERN void @@ -653,21 +674,49 @@ lws_libev_io(struct lws *wsi, int flags); LWS_EXTERN int lws_libev_init_fd_table(struct lws_context *context); LWS_EXTERN void -lws_libev_run(const struct lws_context *context); +lws_libev_run(const struct lws_context *context, int tsi); +#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV) +LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info); #else +#define lws_libev_accept(_a, _b) ((void) 0) +#define lws_libev_io(_a, _b) ((void) 0) +#define lws_libev_init_fd_table(_a) (0) +#define lws_libev_run(_a, _b) ((void) 0) #define LWS_LIBEV_ENABLED(context) (0) -#ifdef LWS_POSIX +#if LWS_POSIX #define lws_feature_status_libev(_a) \ lwsl_notice("libev support not compiled in\n") #else #define lws_feature_status_libev(_a) #endif -#define lws_libev_accept(_a, _b) ((void) 0) -#define lws_libev_io(_a, _b) ((void) 0) -#define lws_libev_init_fd_table(_a) (0) -#define lws_libev_run(_a) ((void) 0) #endif +#if defined(LWS_USE_LIBUV) +LWS_EXTERN void +lws_libuv_accept(struct lws *new_wsi, lws_sockfd_type accept_fd); +LWS_EXTERN void +lws_libuv_io(struct lws *wsi, int flags); +LWS_EXTERN int +lws_libuv_init_fd_table(struct lws_context *context); +LWS_EXTERN void +lws_libuv_run(const struct lws_context *context, int tsi); +#define LWS_LIBUV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBUV) +LWS_EXTERN void lws_feature_status_libuv(struct lws_context_creation_info *info); +#else +#define lws_libuv_accept(_a, _b) ((void) 0) +#define lws_libuv_io(_a, _b) ((void) 0) +#define lws_libuv_init_fd_table(_a) (0) +#define lws_libuv_run(_a, _b) ((void) 0) +#define LWS_LIBUV_ENABLED(context) (0) +#if LWS_POSIX +#define lws_feature_status_libuv(_a) \ + lwsl_notice("libuv support not compiled in\n") +#else +#define lws_feature_status_libuv(_a) +#endif +#endif + + #ifdef LWS_USE_IPV6 #define LWS_IPV6_ENABLED(context) \ (!(context->options & LWS_SERVER_OPTION_DISABLE_IPV6)) @@ -933,10 +982,10 @@ struct lws { /* lifetime members */ -#ifdef LWS_USE_LIBEV +#if defined(LWS_USE_LIBEV) || defined(LWS_USE_LIBUV) struct lws_io_watcher w_read; struct lws_io_watcher w_write; -#endif /* LWS_USE_LIBEV */ +#endif time_t pending_timeout_limit; /* pointers */ diff --git a/lib/server.c b/lib/server.c index 62bf7f934..aa69f6803 100644 --- a/lib/server.c +++ b/lib/server.c @@ -767,6 +767,7 @@ lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) } lws_libev_accept(new_wsi, new_wsi->sock); + lws_libuv_accept(new_wsi, new_wsi->sock); if (!LWS_SSL_ENABLED(context)) { if (insert_wsi_socket_into_fds(context, new_wsi)) @@ -901,6 +902,7 @@ try_pollout: } lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); if (wsi->state != LWSS_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow(wsi->protocol->callback, diff --git a/lib/service.c b/lib/service.c index 5893a69c4..a71d20e40 100644 --- a/lib/service.c +++ b/lib/service.c @@ -238,6 +238,7 @@ user_service: } lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); } #ifdef LWS_USE_HTTP2 diff --git a/lib/ssl.c b/lib/ssl.c index 751f9c193..5a60a4e57 100644 --- a/lib/ssl.c +++ b/lib/ssl.c @@ -665,6 +665,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) goto fail; lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); lws_latency_pre(context, wsi); @@ -733,6 +734,7 @@ go_again: goto fail; lws_libev_io(wsi, LWS_EV_START | LWS_EV_READ); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_READ); lwsl_info("SSL_ERROR_WANT_READ\n"); break; @@ -742,6 +744,7 @@ go_again: goto fail; lws_libev_io(wsi, LWS_EV_START | LWS_EV_WRITE); + lws_libuv_io(wsi, LWS_EV_START | LWS_EV_WRITE); break; } lwsl_debug("SSL_accept failed skt %u: %s\n", diff --git a/lws_config.h.in b/lws_config.h.in index 9d4a0a646..6ce964b6d 100644 --- a/lws_config.h.in +++ b/lws_config.h.in @@ -41,6 +41,9 @@ /* Enable libev io loop */ #cmakedefine LWS_USE_LIBEV +/* Enable libuv io loop */ +#cmakedefine LWS_USE_LIBUV + /* Build with support for ipv6 */ #cmakedefine LWS_USE_IPV6 diff --git a/test-server/test-server-libev.c b/test-server/test-server-libev.c index 0af1d9b13..204874268 100644 --- a/test-server/test-server-libev.c +++ b/test-server/test-server-libev.c @@ -352,7 +352,7 @@ int main(int argc, char **argv) /* override the active fops */ lws_get_fops(context)->open = test_server_fops_open; - lws_initloop(context, loop); + lws_ev_initloop(context, loop, 0); _ev_timer_init(&timeout_watcher, ev_timeout_cb, 0.05, 0.05); ev_timer_start(loop, &timeout_watcher); diff --git a/test-server/test-server-libuv.c b/test-server/test-server-libuv.c new file mode 100644 index 000000000..a04e75d3a --- /dev/null +++ b/test-server/test-server-libuv.c @@ -0,0 +1,362 @@ +/* + * libwebsockets-test-server for libev - libwebsockets test implementation + * + * Copyright (C) 2010-2015 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "test-server.h" + +int close_testing; +int max_poll_elements; +int debug_level = 7; +volatile int force_exit = 0; +struct lws_context *context; +struct lws_plat_file_ops fops_plat; + +/* http server gets files from this path */ +#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" +char *resource_path = LOCAL_RESOURCE_PATH; + +/* + * libev dumps their hygeine problems on their users blaming compiler + * http://lists.schmorp.de/pipermail/libev/2008q4/000442.html + */ + +#if EV_MINPRI == EV_MAXPRI +# define _ev_set_priority(ev, pri) ((ev), (pri)) +#else +# define _ev_set_priority(ev, pri) { \ + ev_watcher *evw = (ev_watcher *)(void *)ev; \ + evw->priority = pri; \ +} +#endif + +#define _ev_init(ev,cb_) { \ + ev_watcher *evw = (ev_watcher *)(void *)ev; \ +\ + evw->active = evw->pending = 0; \ + _ev_set_priority((ev), 0); \ + ev_set_cb((ev), cb_); \ +} + +#define _ev_timer_init(ev, cb, after, _repeat) { \ + ev_watcher_time *evwt = (ev_watcher_time *)(void *)ev; \ +\ + _ev_init(ev, cb); \ + evwt->at = after; \ + (ev)->repeat = _repeat; \ +} + +/* singlethreaded version --> no locks */ + +void test_server_lock(int care) +{ +} +void test_server_unlock(int care) +{ +} + +/* + * This demo server shows how to use libwebsockets for one or more + * websocket protocols in the same server + * + * It defines the following websocket protocols: + * + * dumb-increment-protocol: once the socket is opened, an incrementing + * ascii string is sent down it every 50ms. + * If you send "reset\n" on the websocket, then + * the incrementing number is reset to 0. + * + * lws-mirror-protocol: copies any received packet to every connection also + * using this protocol, including the sender + */ + +enum demo_protocols { + /* always first */ + PROTOCOL_HTTP = 0, + + PROTOCOL_DUMB_INCREMENT, + PROTOCOL_LWS_MIRROR, + + /* always last */ + DEMO_PROTOCOL_COUNT +}; + +/* list of supported protocols and callbacks */ + +static struct lws_protocols protocols[] = { + /* first protocol must always be HTTP handler */ + + { + "http-only", /* name */ + callback_http, /* callback */ + sizeof (struct per_session_data__http), /* per_session_data_size */ + 0, /* max frame size / rx buffer */ + }, + { + "dumb-increment-protocol", + callback_dumb_increment, + sizeof(struct per_session_data__dumb_increment), + 10, + }, + { + "lws-mirror-protocol", + callback_lws_mirror, + sizeof(struct per_session_data__lws_mirror), + 128, + }, + { NULL, NULL, 0, 0 } /* terminator */ +}; + + +/* this shows how to override the lws file operations. You don't need + * to do any of this unless you have a reason (eg, want to serve + * compressed files without decompressing the whole archive) + */ +static lws_filefd_type +test_server_fops_open(struct lws *wsi, const char *filename, + unsigned long *filelen, int flags) +{ + lws_filefd_type n; + + /* call through to original platform implementation */ + n = fops_plat.open(wsi, filename, filelen, flags); + + lwsl_notice("%s: opening %s, ret %ld, len %lu\n", __func__, filename, + (long)n, *filelen); + + return n; +} + +void signal_cb(struct ev_loop *loop, struct ev_signal* watcher, int revents) +{ + lwsl_notice("Signal caught, exiting...\n"); + force_exit = 1; + switch (watcher->signum) { + case SIGTERM: + case SIGINT: + ev_break(loop, EVBREAK_ALL); + break; + default: + signal(SIGABRT, SIG_DFL); + abort(); + break; + } +} + +static void +ev_timeout_cb (EV_P_ ev_timer *w, int revents) +{ + lws_callback_on_writable_all_protocol(context, + &protocols[PROTOCOL_DUMB_INCREMENT]); +} + +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "port", required_argument, NULL, 'p' }, + { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, + { "interface", required_argument, NULL, 'i' }, + { "closetest", no_argument, NULL, 'c' }, + { "libev", no_argument, NULL, 'e' }, +#ifndef LWS_NO_DAEMONIZE + { "daemonize", no_argument, NULL, 'D' }, +#endif + { "resource_path", required_argument, NULL, 'r' }, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int sigs[] = { SIGINT, SIGKILL, SIGTERM, SIGSEGV, SIGFPE }; + struct ev_signal signals[ARRAY_SIZE(sigs)]; + struct ev_loop *loop = ev_default_loop(0); + struct lws_context_creation_info info; + char interface_name[128] = ""; + const char *iface = NULL; + ev_timer timeout_watcher; + char cert_path[1024]; + char key_path[1024]; + int use_ssl = 0; + int opts = 0; + int n = 0; +#ifndef _WIN32 + int syslog_options = LOG_PID | LOG_PERROR; +#endif +#ifndef LWS_NO_DAEMONIZE + int daemonize = 0; +#endif + + /* + * take care to zero down the info struct, he contains random garbaage + * from the stack otherwise + */ + memset(&info, 0, sizeof info); + info.port = 7681; + + while (n >= 0) { + n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL); + if (n < 0) + continue; + switch (n) { + case 'e': + opts |= LWS_SERVER_OPTION_LIBEV; + break; +#ifndef LWS_NO_DAEMONIZE + case 'D': + daemonize = 1; + #ifndef _WIN32 + syslog_options &= ~LOG_PERROR; + #endif + break; +#endif + case 'd': + debug_level = atoi(optarg); + break; + case 's': + use_ssl = 1; + break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'i': + strncpy(interface_name, optarg, sizeof interface_name); + interface_name[(sizeof interface_name) - 1] = '\0'; + iface = interface_name; + break; + case 'c': + close_testing = 1; + fprintf(stderr, " Close testing mode -- closes on " + "client after 50 dumb increments" + "and suppresses lws_mirror spam\n"); + break; + case 'r': + resource_path = optarg; + printf("Setting resource path to \"%s\"\n", resource_path); + break; + case 'h': + fprintf(stderr, "Usage: test-server " + "[--port=

] [--ssl] " + "[-d ] " + "[--resource_path ]\n"); + exit(1); + } + } + +#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32) + /* + * normally lock path would be /var/lock/lwsts or similar, to + * simplify getting started without having to take care about + * permissions or running as root, set to /tmp/.lwsts-lock + */ + if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) { + fprintf(stderr, "Failed to daemonize\n"); + return 1; + } +#endif + + for (n = 0; n < ARRAY_SIZE(sigs); n++) { + _ev_init(&signals[n], signal_cb); + ev_signal_set(&signals[n], sigs[n]); + ev_signal_start(loop, &signals[n]); + } + +#ifndef _WIN32 + /* we will only try to log things according to our debug_level */ + setlogmask(LOG_UPTO (LOG_DEBUG)); + openlog("lwsts", syslog_options, LOG_DAEMON); +#endif + + /* tell the library what debug level to emit and to send it to syslog */ + lws_set_log_level(debug_level, lwsl_emit_syslog); + + lwsl_notice("libwebsockets test server libuv - license LGPL2.1+SLE\n"); + lwsl_notice("(C) Copyright 2010-2016 Andy Green \n"); + + printf("Using resource path \"%s\"\n", resource_path); + + info.iface = iface; + info.protocols = protocols; +#ifndef LWS_NO_EXTENSIONS + info.extensions = lws_get_internal_extensions(); +#endif + + info.ssl_cert_filepath = NULL; + info.ssl_private_key_filepath = NULL; + + if (use_ssl) { + if (strlen(resource_path) > sizeof(cert_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(cert_path, "%s/libwebsockets-test-server.pem", + resource_path); + if (strlen(resource_path) > sizeof(key_path) - 32) { + lwsl_err("resource path too long\n"); + return -1; + } + sprintf(key_path, "%s/libwebsockets-test-server.key.pem", + resource_path); + + info.ssl_cert_filepath = cert_path; + info.ssl_private_key_filepath = key_path; + } + info.gid = -1; + info.uid = -1; + info.max_http_header_pool = 1; + info.options = opts | LWS_SERVER_OPTION_LIBUV; + + context = lws_create_context(&info); + if (context == NULL) { + lwsl_err("libwebsocket init failed\n"); + return -1; + } + + /* + * this shows how to override the lws file operations. You don't need + * to do any of this unless you have a reason (eg, want to serve + * compressed files without decompressing the whole archive) + */ + /* stash original platform fops */ + fops_plat = *(lws_get_fops(context)); + /* override the active fops */ + lws_get_fops(context)->open = test_server_fops_open; + + lws_ev_initloop(context, loop, 0); + + _ev_timer_init(&timeout_watcher, ev_timeout_cb, 0.05, 0.05); + ev_timer_start(loop, &timeout_watcher); + + while (!force_exit) + ev_run(loop, 0); + + lws_context_destroy(context); + ev_loop_destroy(loop); + + lwsl_notice("libwebsockets-test-server exited cleanly\n"); + +#ifndef _WIN32 + closelog(); +#endif + + return 0; +}