test servers: convert to static inclusion of plugins
[platform/upstream/libwebsockets.git] / test-server / test-server.c
index ad2e9cd..f4c4bc6 100644 (file)
@@ -1,64 +1,81 @@
 /*
  * libwebsockets-test-server - libwebsockets test implementation
  *
- * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
  *
- *  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 file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
  *
- *  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.
+ * 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.
  *
- *  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
+ * 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.
  */
-#ifdef CMAKE_BUILD
-#include "lws_config.h"
-#endif
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <assert.h>
-#ifdef WIN32
+#include "test-server.h"
+
+int close_testing;
+int max_poll_elements;
+int debug_level = 7;
 
 #ifdef EXTERNAL_POLL
-       #ifndef WIN32_LEAN_AND_MEAN
-       #define WIN32_LEAN_AND_MEAN
-       #endif
-       #include <winsock2.h>
-       #include <ws2tcpip.h>
-       #include <stddef.h>
-
-       #include "websock-w32.h"
+struct lws_pollfd *pollfds;
+int *fd_lookup;
+int count_pollfds;
 #endif
+volatile int force_exit = 0;
+struct lws_context *context;
+struct lws_plat_file_ops fops_plat;
 
-#else // NOT WIN32
-#include <syslog.h>
+/* http server gets files from this path */
+#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
+char *resource_path = LOCAL_RESOURCE_PATH;
+#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
+char crl_path[1024] = "";
 #endif
 
-#include <signal.h>
+/*
+ * This demonstrates how to use the clean protocol service separation of
+ * plugins, but with static inclusion instead of runtime dynamic loading
+ * (which requires libuv).
+ *
+ * dumb-increment doesn't use the plugin, both to demonstrate how to
+ * do the protocols directly, and because it wants libuv for a timer.
+ *
+ * Please consider using test-server-v2.0.c instead of this: it has the
+ * same functionality but
+ *
+ * 1) uses lws built-in http handling so you don't need to deal with it in
+ * your callback
+ *
+ * 2) Links with libuv and uses the plugins at runtime
+ *
+ * 3) Uses advanced lws features like mounts to bind parts of the filesystem
+ * to the served URL space
+ *
+ * Another option is lwsws, this operates like test-server-v2,0.c but is
+ * configured using JSON, do you do not need to provide any code for the
+ * serving action at all, just implement your protocols in plugins.
+ */
 
-#include "../lib/libwebsockets.h"
+#define LWS_PLUGIN_STATIC
+#include "../plugins/protocol_lws_mirror.c"
+#include "../plugins/protocol_lws_status.c"
 
-static int close_testing;
-int max_poll_elements;
+/* singlethreaded version --> no locks */
 
-struct pollfd *pollfds;
-int *fd_lookup;
-int count_pollfds;
-int force_exit = 0;
+void test_server_lock(int care)
+{
+}
+void test_server_unlock(int care)
+{
+}
 
 /*
  * This demo server shows how to use libwebsockets for one or more
@@ -73,6 +90,9 @@ int force_exit = 0;
  *
  *  lws-mirror-protocol: copies any received packet to every connection also
  *                             using this protocol, including the sender
+ *
+ *  lws-status:                        informs connected browsers of who else is
+ *                             connected.
  */
 
 enum demo_protocols {
@@ -81,497 +101,15 @@ enum demo_protocols {
 
        PROTOCOL_DUMB_INCREMENT,
        PROTOCOL_LWS_MIRROR,
+       PROTOCOL_LWS_STATUS,
 
        /* always last */
        DEMO_PROTOCOL_COUNT
 };
 
-
-#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
-
-/*
- * We take a strict whitelist approach to stop ../ attacks
- */
-
-struct serveable {
-       const char *urlpath;
-       const char *mimetype;
-}; 
-
-static const struct serveable whitelist[] = {
-       { "/favicon.ico", "image/x-icon" },
-       { "/libwebsockets.org-logo.png", "image/png" },
-
-       /* last one is the default served if no match */
-       { "/test.html", "text/html" },
-};
-
-struct per_session_data__http {
-       int fd;
-};
-
-/* this protocol server (always the first one) just knows how to do HTTP */
-
-static int callback_http(struct libwebsocket_context *context,
-               struct libwebsocket *wsi,
-               enum libwebsocket_callback_reasons reason, void *user,
-                                                          void *in, size_t len)
-{
-#if 0
-       char client_name[128];
-       char client_ip[128];
-#endif
-       char buf[256];
-       int n, m;
-       unsigned char *p;
-       static unsigned char buffer[4096];
-       struct stat stat_buf;
-       struct per_session_data__http *pss = (struct per_session_data__http *)user;
-#ifdef EXTERNAL_POLL
-       int fd = (int)(long)in;
-#endif
-
-       switch (reason) {
-       case LWS_CALLBACK_HTTP:
-
-               /* check for the "send a big file by hand" example case */
-
-               if (!strcmp((const char *)in, "/leaf.jpg")) {
-
-                       /* well, let's demonstrate how to send the hard way */
-
-                       p = buffer;
-
-#ifdef WIN32
-                       pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY | _O_BINARY);
-#else
-                       pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY);
-#endif
-
-                       if (pss->fd < 0)
-                               return -1;
-
-                       fstat(pss->fd, &stat_buf);
-
-                       /*
-                        * we will send a big jpeg file, but it could be
-                        * anything.  Set the Content-Type: appropriately
-                        * so the browser knows what to do with it.
-                        */
-
-                       p += sprintf((char *)p,
-                               "HTTP/1.0 200 OK\x0d\x0a"
-                               "Server: libwebsockets\x0d\x0a"
-                               "Content-Type: image/jpeg\x0d\x0a"
-                                       "Content-Length: %u\x0d\x0a\x0d\x0a",
-                                       (unsigned int)stat_buf.st_size);
-
-                       /*
-                        * send the http headers...
-                        * this won't block since it's the first payload sent
-                        * on the connection since it was established
-                        * (too small for partial)
-                        */
-
-                       n = libwebsocket_write(wsi, buffer,
-                                  p - buffer, LWS_WRITE_HTTP);
-
-                       if (n < 0) {
-                               close(pss->fd);
-                               return -1;
-                       }
-                       /*
-                        * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
-                        */
-                       libwebsocket_callback_on_writable(context, wsi);
-                       break;
-               }
-
-               /* if not, send a file the easy way */
-
-               for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
-                       if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
-                               break;
-
-               sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath);
-
-               if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
-                       return -1; /* through completion or error, close the socket */
-
-               /*
-                * notice that the sending of the file completes asynchronously,
-                * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
-                * it's done
-                */
-
-               break;
-
-       case LWS_CALLBACK_HTTP_FILE_COMPLETION:
-//             lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
-               /* kill the connection after we sent one file */
-               return -1;
-
-       case LWS_CALLBACK_HTTP_WRITEABLE:
-               /*
-                * we can send more of whatever it is we were sending
-                */
-
-               do {
-                       n = read(pss->fd, buffer, sizeof buffer);
-                       /* problem reading, close conn */
-                       if (n < 0)
-                               goto bail;
-                       /* sent it all, close conn */
-                       if (n == 0)
-                               goto bail;
-                       /*
-                        * because it's HTTP and not websocket, don't need to take
-                        * care about pre and postamble
-                        */
-                       m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
-                       if (m < 0)
-                               /* write failed, close conn */
-                               goto bail;
-                       if (m != n)
-                               /* partial write, adjust */
-                               lseek(pss->fd, m - n, SEEK_CUR);
-
-               } while (!lws_send_pipe_choked(wsi));
-               libwebsocket_callback_on_writable(context, wsi);
-               break;
-
-bail:
-               close(pss->fd);
-               return -1;
-
-       /*
-        * callback for confirming to continue with client IP appear in
-        * protocol 0 callback since no websocket protocol has been agreed
-        * yet.  You can just ignore this if you won't filter on client IP
-        * since the default uhandled callback return is 0 meaning let the
-        * connection continue.
-        */
-
-       case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-#if 0
-               libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
-                            sizeof(client_name), client_ip, sizeof(client_ip));
-
-               fprintf(stderr, "Received network connect from %s (%s)\n",
-                                                       client_name, client_ip);
-#endif
-               /* if we returned non-zero from here, we kill the connection */
-               break;
-
-#ifdef EXTERNAL_POLL
-       /*
-        * callbacks for managing the external poll() array appear in
-        * protocol 0 callback
-        */
-
-       case LWS_CALLBACK_ADD_POLL_FD:
-
-               if (count_pollfds >= max_poll_elements) {
-                       lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
-                       return 1;
-               }
-
-               fd_lookup[fd] = count_pollfds;
-               pollfds[count_pollfds].fd = fd;
-               pollfds[count_pollfds].events = (int)(long)len;
-               pollfds[count_pollfds++].revents = 0;
-               break;
-
-       case LWS_CALLBACK_DEL_POLL_FD:
-               if (!--count_pollfds)
-                       break;
-               m = fd_lookup[fd];
-               /* have the last guy take up the vacant slot */
-               pollfds[m] = pollfds[count_pollfds];
-               fd_lookup[pollfds[count_pollfds].fd] = m;
-               break;
-
-       case LWS_CALLBACK_SET_MODE_POLL_FD:
-               pollfds[fd_lookup[fd]].events |= (int)(long)len;
-               break;
-
-       case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
-               pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
-               break;
-#endif
-
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-/*
- * this is just an example of parsing handshake headers, you don't need this
- * in your code unless you will filter allowing connections by the header
- * content
- */
-
-static void
-dump_handshake_info(struct libwebsocket *wsi)
-{
-       int n;
-       static const char *token_names[WSI_TOKEN_COUNT] = {
-               /*[WSI_TOKEN_GET_URI]           =*/ "GET URI",
-               /*[WSI_TOKEN_HOST]              =*/ "Host",
-               /*[WSI_TOKEN_CONNECTION]        =*/ "Connection",
-               /*[WSI_TOKEN_KEY1]              =*/ "key 1",
-               /*[WSI_TOKEN_KEY2]              =*/ "key 2",
-               /*[WSI_TOKEN_PROTOCOL]          =*/ "Protocol",
-               /*[WSI_TOKEN_UPGRADE]           =*/ "Upgrade",
-               /*[WSI_TOKEN_ORIGIN]            =*/ "Origin",
-               /*[WSI_TOKEN_DRAFT]             =*/ "Draft",
-               /*[WSI_TOKEN_CHALLENGE]         =*/ "Challenge",
-
-               /* new for 04 */
-               /*[WSI_TOKEN_KEY]               =*/ "Key",
-               /*[WSI_TOKEN_VERSION]           =*/ "Version",
-               /*[WSI_TOKEN_SWORIGIN]          =*/ "Sworigin",
-
-               /* new for 05 */
-               /*[WSI_TOKEN_EXTENSIONS]        =*/ "Extensions",
-
-               /* client receives these */
-               /*[WSI_TOKEN_ACCEPT]            =*/ "Accept",
-               /*[WSI_TOKEN_NONCE]             =*/ "Nonce",
-               /*[WSI_TOKEN_HTTP]              =*/ "Http",
-               /*[WSI_TOKEN_MUXURL]    =*/ "MuxURL",
-       };
-       char buf[256];
-
-       for (n = 0; n < WSI_TOKEN_COUNT; n++) {
-               if (!lws_hdr_total_length(wsi, n))
-                       continue;
-
-               lws_hdr_copy(wsi, buf, sizeof buf, n);
-
-               fprintf(stderr, "    %s = %s\n", token_names[n], buf);
-       }
-}
-
-/* dumb_increment protocol */
-
-/*
- * one of these is auto-created for each connection and a pointer to the
- * appropriate instance is passed to the callback in the user parameter
- *
- * for this example protocol we use it to individualize the count for each
- * connection.
- */
-
-struct per_session_data__dumb_increment {
-       int number;
-};
-
-static int
-callback_dumb_increment(struct libwebsocket_context *context,
-                       struct libwebsocket *wsi,
-                       enum libwebsocket_callback_reasons reason,
-                                              void *user, void *in, size_t len)
-{
-       int n;
-       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
-                                                 LWS_SEND_BUFFER_POST_PADDING];
-       unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
-       struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
-
-       switch (reason) {
-
-       case LWS_CALLBACK_ESTABLISHED:
-               lwsl_info("callback_dumb_increment: "
-                                                "LWS_CALLBACK_ESTABLISHED\n");
-               pss->number = 0;
-               break;
-
-       case LWS_CALLBACK_SERVER_WRITEABLE:
-               n = sprintf((char *)p, "%d", pss->number++);
-               /* too small for partial */
-               n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
-               if (n < 0) {
-                       lwsl_err("ERROR %d writing to di socket\n", n);
-                       return -1;
-               }
-               if (close_testing && pss->number == 50) {
-                       lwsl_info("close tesing limit, closing\n");
-                       return -1;
-               }
-               break;
-
-       case LWS_CALLBACK_RECEIVE:
-//             fprintf(stderr, "rx %d\n", (int)len);
-               if (len < 6)
-                       break;
-               if (strcmp((const char *)in, "reset\n") == 0)
-                       pss->number = 0;
-               break;
-       /*
-        * this just demonstrates how to use the protocol filter. If you won't
-        * study and reject connections based on header content, you don't need
-        * to handle this callback
-        */
-
-       case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
-               dump_handshake_info(wsi);
-               /* you could return non-zero here and kill the connection */
-               break;
-
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-
-/* lws-mirror_protocol */
-
-#define MAX_MESSAGE_QUEUE 128
-
-struct per_session_data__lws_mirror {
-       struct libwebsocket *wsi;
-       int ringbuffer_tail;
-};
-
-struct a_message {
-       void *payload;
-       size_t len;
-};
-
-static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
-static int ringbuffer_head;
-
-static struct libwebsocket *wsi_choked[20];
-static int num_wsi_choked;
-
-static int
-callback_lws_mirror(struct libwebsocket_context *context,
-                       struct libwebsocket *wsi,
-                       enum libwebsocket_callback_reasons reason,
-                                              void *user, void *in, size_t len)
-{
-       int n;
-       struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
-
-       switch (reason) {
-
-       case LWS_CALLBACK_ESTABLISHED:
-               lwsl_info("callback_lws_mirror: "
-                                                "LWS_CALLBACK_ESTABLISHED\n");
-               pss->ringbuffer_tail = ringbuffer_head;
-               pss->wsi = wsi;
-               break;
-
-       case LWS_CALLBACK_PROTOCOL_DESTROY:
-               lwsl_notice("mirror protocol cleaning up\n");
-               for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
-                       if (ringbuffer[n].payload)
-                               free(ringbuffer[n].payload);
-               break;
-
-       case LWS_CALLBACK_SERVER_WRITEABLE:
-               if (close_testing)
-                       break;
-               while (pss->ringbuffer_tail != ringbuffer_head) {
-
-                       n = libwebsocket_write(wsi, (unsigned char *)
-                                  ringbuffer[pss->ringbuffer_tail].payload +
-                                  LWS_SEND_BUFFER_PRE_PADDING,
-                                  ringbuffer[pss->ringbuffer_tail].len,
-                                                               LWS_WRITE_TEXT);
-                       if (n < 0) {
-                               lwsl_err("ERROR %d writing to mirror socket\n", n);
-                               return -1;
-                       }
-                       if (n < ringbuffer[pss->ringbuffer_tail].len) {
-                               lwsl_err("mirror partial write %d vs %d\n", n, ringbuffer[pss->ringbuffer_tail].len);
-                       }
-
-                       if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
-                               pss->ringbuffer_tail = 0;
-                       else
-                               pss->ringbuffer_tail++;
-
-                       if (((ringbuffer_head - pss->ringbuffer_tail) &
-                                 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) {
-                               for (n = 0; n < num_wsi_choked; n++)
-                                       libwebsocket_rx_flow_control(wsi_choked[n], 1);
-                               num_wsi_choked = 0;
-                       }
-                       // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-
-                       if (lws_send_pipe_choked(wsi)) {
-                               libwebsocket_callback_on_writable(context, wsi);
-                               return 0;
-                       }
-               }
-               break;
-
-       case LWS_CALLBACK_RECEIVE:
-
-               if (((ringbuffer_head - pss->ringbuffer_tail) &
-                                 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
-                       lwsl_err("dropping!\n");
-                       goto choke;
-               }
-
-               if (ringbuffer[ringbuffer_head].payload)
-                       free(ringbuffer[ringbuffer_head].payload);
-
-               ringbuffer[ringbuffer_head].payload =
-                               malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
-                                                 LWS_SEND_BUFFER_POST_PADDING);
-               ringbuffer[ringbuffer_head].len = len;
-               memcpy((char *)ringbuffer[ringbuffer_head].payload +
-                                         LWS_SEND_BUFFER_PRE_PADDING, in, len);
-               if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
-                       ringbuffer_head = 0;
-               else
-                       ringbuffer_head++;
-
-               if (((ringbuffer_head - pss->ringbuffer_tail) &
-                                 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
-                       goto done;
-
-choke:
-               if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) {
-                       libwebsocket_rx_flow_control(wsi, 0);
-                       wsi_choked[num_wsi_choked++] = wsi;
-               }
-
-//             lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-done:
-               libwebsocket_callback_on_writable_all_protocol(
-                                              libwebsockets_get_protocol(wsi));
-               break;
-
-       /*
-        * this just demonstrates how to use the protocol filter. If you won't
-        * study and reject connections based on header content, you don't need
-        * to handle this callback
-        */
-
-       case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
-               dump_handshake_info(wsi);
-               /* you could return non-zero here and kill the connection */
-               break;
-
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-
 /* list of supported protocols and callbacks */
 
-static struct libwebsocket_protocols protocols[] = {
+static struct lws_protocols protocols[] = {
        /* first protocol must always be HTTP handler */
 
        {
@@ -584,76 +122,154 @@ static struct libwebsocket_protocols protocols[] = {
                "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,
+               10, /* rx buf size must be >= permessage-deflate rx size
+                    * dumb-increment only sends very small packets, so we set
+                    * this accordingly.  If your protocol will send bigger
+                    * things, adjust this to match */
        },
+       LWS_PLUGIN_PROTOCOL_MIRROR,
+       LWS_PLUGIN_PROTOCOL_LWS_STATUS,
        { 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_fop_fd_t
+test_server_fops_open(const struct lws_plat_file_ops *fops,
+                    const char *vfs_path, const char *vpath,
+                    lws_fop_flags_t *flags)
+{
+       lws_fop_fd_t fop_fd;
+
+       /* call through to original platform implementation */
+       fop_fd = fops_plat.open(fops, vfs_path, vpath, flags);
+
+       if (fop_fd)
+               lwsl_info("%s: opening %s, ret %p, len %lu\n", __func__,
+                               vfs_path, fop_fd,
+                               (long)lws_vfs_get_length(fop_fd));
+       else
+               lwsl_info("%s: open %s failed\n", __func__, vfs_path);
+
+       return fop_fd;
+}
+
 void sighandler(int sig)
 {
        force_exit = 1;
+       lws_cancel_service(context);
 }
 
+static const struct lws_extension exts[] = {
+       {
+               "permessage-deflate",
+               lws_extension_callback_pm_deflate,
+               "permessage-deflate"
+       },
+       {
+               "deflate-frame",
+               lws_extension_callback_pm_deflate,
+               "deflate_frame"
+       },
+       { NULL, NULL, NULL /* terminator */ }
+};
+
+
+
 static struct option options[] = {
        { "help",       no_argument,            NULL, 'h' },
        { "debug",      required_argument,      NULL, 'd' },
        { "port",       required_argument,      NULL, 'p' },
        { "ssl",        no_argument,            NULL, 's' },
-       { "interface",  required_argument,      NULL, 'i' },
-       { "closetest",  no_argument,            NULL, 'c' },
+       { "allow-non-ssl",      no_argument,    NULL, 'a' },
+       { "interface",  required_argument,      NULL, 'i' },
+       { "closetest",  no_argument,            NULL, 'c' },
+       { "ssl-cert",  required_argument,       NULL, 'C' },
+       { "ssl-key",  required_argument,        NULL, 'K' },
+       { "ssl-ca",  required_argument,         NULL, 'A' },
+#if defined(LWS_OPENSSL_SUPPORT)
+       { "ssl-verify-client",  no_argument,            NULL, 'v' },
+#if defined(LWS_HAVE_SSL_CTX_set1_param)
+       { "ssl-crl",  required_argument,                NULL, 'R' },
+#endif
+#endif
+       { "libev",  no_argument,                NULL, 'e' },
 #ifndef LWS_NO_DAEMONIZE
-       { "daemonize",  no_argument,            NULL, 'D' },
+       { "daemonize",  no_argument,            NULL, 'D' },
 #endif
+       { "resource_path", required_argument,   NULL, 'r' },
+       { "pingpong-secs", required_argument,   NULL, 'P' },
        { NULL, 0, 0, 0 }
 };
 
 int main(int argc, char **argv)
 {
-       int n = 0;
-       int use_ssl = 0;
-       struct libwebsocket_context *context;
-       int opts = 0;
+       struct lws_context_creation_info info;
+       struct lws_vhost *vhost;
        char interface_name[128] = "";
+       unsigned int ms, oldms = 0;
        const char *iface = NULL;
-#ifndef WIN32
+       char cert_path[1024] = "";
+       char key_path[1024] = "";
+       char ca_path[1024] = "";
+       int uid = -1, gid = -1;
+       int use_ssl = 0;
+       int pp_secs = 0;
+       int opts = 0;
+       int n = 0;
+#ifndef _WIN32
+/* LOG_PERROR is not POSIX standard, and may not be portable */
+#ifdef __sun
+       int syslog_options = LOG_PID;
+#else       
        int syslog_options = LOG_PID | LOG_PERROR;
 #endif
-       unsigned int oldus = 0;
-       struct lws_context_creation_info info;
-
-       int debug_level = 7;
+#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, "ci:hsp:d:D", options, NULL);
+               n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:k", 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
+                       #if !defined(_WIN32) && !defined(__sun)
                        syslog_options &= ~LOG_PERROR;
                        #endif
                        break;
 #endif
+               case 'u':
+                       uid = atoi(optarg);
+                       break;
+               case 'g':
+                       gid = atoi(optarg);
+                       break;
                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;
@@ -662,35 +278,76 @@ int main(int argc, char **argv)
                        interface_name[(sizeof interface_name) - 1] = '\0';
                        iface = interface_name;
                        break;
+               case 'k':
+                       info.bind_iface = 1;
+#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
+                       info.caps[0] = CAP_NET_RAW;
+                       info.count_caps = 1;
+#endif
+                       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 'C':
+                       strncpy(cert_path, optarg, sizeof(cert_path) - 1);
+                       cert_path[sizeof(cert_path) - 1] = '\0';
+                       break;
+               case 'K':
+                       strncpy(key_path, optarg, sizeof(key_path) - 1);
+                       key_path[sizeof(key_path) - 1] = '\0';
+                       break;
+               case 'A':
+                       strncpy(ca_path, optarg, sizeof(ca_path) - 1);
+                       ca_path[sizeof(ca_path) - 1] = '\0';
+                       break;
+               case 'P':
+                       pp_secs = atoi(optarg);
+                       lwsl_notice("Setting pingpong interval to %d\n", pp_secs);
+                       break;
+#if defined(LWS_OPENSSL_SUPPORT)
+               case 'v':
+                       use_ssl = 1;
+                       opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
+                       break;
+
+#if defined(LWS_HAVE_SSL_CTX_set1_param)
+               case 'R':
+                       strncpy(crl_path, optarg, sizeof(crl_path) - 1);
+                       crl_path[sizeof(crl_path) - 1] = '\0';
+                       break;
+#endif
+#endif
                case 'h':
                        fprintf(stderr, "Usage: test-server "
                                        "[--port=<p>] [--ssl] "
-                                       "[-d <log bitfield>]\n");
+                                       "[-d <log bitfield>] "
+                                       "[--resource_path <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;
+               return 10;
        }
 #endif
 
        signal(SIGINT, sighandler);
 
-#ifndef WIN32
+#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);
@@ -699,12 +356,13 @@ int main(int argc, char **argv)
        /* 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-2013 Andy Green <andy@warmcat.com> - "
-                                                   "licensed under LGPL2.1\n");
+       lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n");
+       lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
+
+       printf("Using resource path \"%s\"\n", resource_path);
 #ifdef EXTERNAL_POLL
        max_poll_elements = getdtablesize();
-       pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
+       pollfds = malloc(max_poll_elements * sizeof (struct lws_pollfd));
        fd_lookup = malloc(max_poll_elements * sizeof (int));
        if (pollfds == NULL || fd_lookup == NULL) {
                lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
@@ -714,27 +372,84 @@ int main(int argc, char **argv)
 
        info.iface = iface;
        info.protocols = protocols;
-#ifndef LWS_NO_EXTENSIONS
-       info.extensions = libwebsocket_get_internal_extensions();
-#endif
-       if (!use_ssl) {
-               info.ssl_cert_filepath = NULL;
-               info.ssl_private_key_filepath = NULL;
-       } else {
-               info.ssl_cert_filepath = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
-               info.ssl_private_key_filepath = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
-       }
-       info.gid = -1;
-       info.uid = -1;
-       info.options = opts;
+       info.ssl_cert_filepath = NULL;
+       info.ssl_private_key_filepath = NULL;
+       info.ws_ping_pong_interval = pp_secs;
 
-       context = libwebsocket_create_context(&info);
+       if (use_ssl) {
+               if (strlen(resource_path) > sizeof(cert_path) - 32) {
+                       lwsl_err("resource path too long\n");
+                       return -1;
+               }
+               if (!cert_path[0])
+                       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;
+               }
+               if (!key_path[0])
+                       sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
+                                                               resource_path);
+
+               info.ssl_cert_filepath = cert_path;
+               info.ssl_private_key_filepath = key_path;
+               if (ca_path[0])
+                       info.ssl_ca_filepath = ca_path;
+       }
+       info.gid = gid;
+       info.uid = uid;
+       info.max_http_header_pool = 16;
+       info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+       info.extensions = exts;
+       info.timeout_secs = 5;
+       info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
+                              "ECDHE-RSA-AES256-GCM-SHA384:"
+                              "DHE-RSA-AES256-GCM-SHA384:"
+                              "ECDHE-RSA-AES256-SHA384:"
+                              "HIGH:!aNULL:!eNULL:!EXPORT:"
+                              "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
+                              "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
+                              "!DHE-RSA-AES128-SHA256:"
+                              "!AES128-GCM-SHA256:"
+                              "!AES128-SHA256:"
+                              "!DHE-RSA-AES256-SHA256:"
+                              "!AES256-GCM-SHA384:"
+                              "!AES256-SHA256";
+
+       if (use_ssl)
+               /* redirect guys coming on http */
+               info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
+
+       context = lws_create_context(&info);
        if (context == NULL) {
                lwsl_err("libwebsocket init failed\n");
                return -1;
        }
 
+       vhost = lws_create_vhost(context, &info);
+       if (!vhost) {
+               lwsl_err("vhost creation failed\n");
+               return -1;
+       }
+
+#if !defined(LWS_NO_CLIENT) && defined(LWS_OPENSSL_SUPPORT)
+       lws_init_vhost_client_ssl(&info, vhost);
+#endif
+
+       /* 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;
+
        n = 0;
+#ifdef EXTERNAL_POLL
+       int ms_1sec = 0;
+#endif
        while (n >= 0 && !force_exit) {
                struct timeval tv;
 
@@ -746,13 +461,14 @@ int main(int argc, char **argv)
                 * as soon as it can take more packets (usually immediately)
                 */
 
-               if (((unsigned int)tv.tv_usec - oldus) > 50000) {
-                       libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
-                       oldus = tv.tv_usec;
+               ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+               if ((ms - oldms) > 50) {
+                       lws_callback_on_writable_all_protocol(context,
+                               &protocols[PROTOCOL_DUMB_INCREMENT]);
+                       oldms = ms;
                }
 
 #ifdef EXTERNAL_POLL
-
                /*
                 * this represents an existing server's single poll action
                 * which also includes libwebsocket sockets
@@ -762,8 +478,7 @@ int main(int argc, char **argv)
                if (n < 0)
                        continue;
 
-
-               if (n)
+               if (n) {
                        for (n = 0; n < count_pollfds; n++)
                                if (pollfds[n].revents)
                                        /*
@@ -771,9 +486,23 @@ int main(int argc, char **argv)
                                        * match anything under libwebsockets
                                        * control
                                        */
-                                       if (libwebsocket_service_fd(context,
+                                       if (lws_service_fd(context,
                                                                  &pollfds[n]) < 0)
                                                goto done;
+
+                       /* if needed, force-service wsis that may not have read all input */
+                       while (!lws_service_adjust_timeout(context, 1, 0)) {
+                               lwsl_notice("extpoll doing forced service!\n");
+                               lws_service_tsi(context, -1, 0);
+                       }
+               } else {
+                       /* no revents, but before polling again, make lws check for any timeouts */
+                       if (ms - ms_1sec > 1000) {
+                               lwsl_notice("1 per sec\n");
+                               lws_service_fd(context, NULL);
+                               ms_1sec = ms;
+                       }
+               }
 #else
                /*
                 * If libwebsockets sockets are all we care about,
@@ -784,7 +513,7 @@ int main(int argc, char **argv)
                 * the number of ms in the second argument.
                 */
 
-               n = libwebsocket_service(context, 50);
+               n = lws_service(context, 50);
 #endif
        }
 
@@ -792,11 +521,11 @@ int main(int argc, char **argv)
 done:
 #endif
 
-       libwebsocket_context_destroy(context);
+       lws_context_destroy(context);
 
        lwsl_notice("libwebsockets-test-server exited cleanly\n");
 
-#ifndef WIN32
+#ifndef _WIN32
        closelog();
 #endif