diff --git a/CMakeLists.txt b/CMakeLists.txt index a098a95b..35acd58d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -776,6 +776,10 @@ if (NOT LWS_WITHOUT_TESTAPPS) if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))) create_test_app(test-server-pthreads "test-server/test-server-pthreads.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c") endif() + if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + AND LWS_WITH_LIBEV) + create_test_app(test-server-libev "test-server/test-server-libev.c" "test-server/test-server-http.c" "test-server/test-server-dumb-increment.c" "test-server/test-server-mirror.c") + endif() endif() # diff --git a/test-server/test-server-libev.c b/test-server/test-server-libev.c new file mode 100644 index 00000000..de9e12ef --- /dev/null +++ b/test-server/test-server-libev.c @@ -0,0 +1,363 @@ +/* + * 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; +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 debug_level = 7; + 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 - " + "(C) Copyright 2010-2015 Andy Green - " + "licensed under LGPL2.1\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_LIBEV; + + 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_initloop(context, loop); + + _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; +}