packaging: support smack manifest and cleanup
[profile/ivi/libwebsockets.git] / test-server / test-server.c
index 857cae2..6af7a99 100644 (file)
@@ -18,6 +18,9 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA  02110-1301  USA
  */
+#ifdef CMAKE_BUILD
+#include "lws_config.h"
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <getopt.h>
 #include <string.h>
 #include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <assert.h>
+#ifdef WIN32
 
-#include "../lib/libwebsockets.h"
+#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"
+#endif
 
-static int close_testing;
+#else // NOT WIN32
+#include <syslog.h>
+#endif
 
-#ifdef EXTERNAL_POLL
-#define LWS_NO_FORK
+#include <signal.h>
 
-#define MAX_POLL_ELEMENTS 32000
+#include "../lib/libwebsockets.h"
 
-struct pollfd pollfds[MAX_POLL_ELEMENTS];
-int fd_lookup[MAX_POLL_ELEMENTS];
-int count_pollfds;
+static int close_testing;
+int max_poll_elements;
 
-#endif /* EXTERNAL_POLL */
+struct pollfd *pollfds;
+int *fd_lookup;
+int count_pollfds;
+int force_exit = 0;
 
 /*
  * This demo server shows how to use libwebsockets for one or more
@@ -88,6 +106,10 @@ static const struct serveable whitelist[] = {
        { "/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,
@@ -95,26 +117,81 @@ static int callback_http(struct libwebsocket_context *context,
                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;
+       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 m;
-       int fd = (int)(long)user;
+       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;
+
+                       pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY);
+                       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(in, whitelist[n].urlpath) == 0)
+                       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))
-                       fprintf(stderr, "Failed to send HTTP file\n");
+                       return -1; /* through completion or error, close the socket */
 
                /*
                 * notice that the sending of the file completes asynchronously,
@@ -125,9 +202,42 @@ static int callback_http(struct libwebsocket_context *context,
                break;
 
        case LWS_CALLBACK_HTTP_FILE_COMPLETION:
-//             fprintf(stderr, "LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
+//             lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
                /* kill the connection after we sent one file */
-               return 1;
+               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
@@ -138,13 +248,13 @@ static int callback_http(struct libwebsocket_context *context,
         */
 
        case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-
-               libwebsockets_get_peer_addresses((int)(long)user, client_name,
+#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);
-
+               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;
 
@@ -156,8 +266,8 @@ static int callback_http(struct libwebsocket_context *context,
 
        case LWS_CALLBACK_ADD_POLL_FD:
 
-               if (count_pollfds >= MAX_POLL_ELEMENTS) {
-                       fprintf(stderr, "LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
+               if (count_pollfds >= max_poll_elements) {
+                       lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
                        return 1;
                }
 
@@ -199,7 +309,7 @@ static int callback_http(struct libwebsocket_context *context,
  */
 
 static void
-dump_handshake_info(struct lws_tokens *lwst)
+dump_handshake_info(struct libwebsocket *wsi)
 {
        int n;
        static const char *token_names[WSI_TOKEN_COUNT] = {
@@ -228,12 +338,15 @@ dump_handshake_info(struct lws_tokens *lwst)
                /*[WSI_TOKEN_HTTP]              =*/ "Http",
                /*[WSI_TOKEN_MUXURL]    =*/ "MuxURL",
        };
+       char buf[256];
 
        for (n = 0; n < WSI_TOKEN_COUNT; n++) {
-               if (lwst[n].token == NULL)
+               if (!lws_hdr_total_length(wsi, n))
                        continue;
 
-               fprintf(stderr, "    %s = %s\n", token_names[n], lwst[n].token);
+               lws_hdr_copy(wsi, buf, sizeof buf, n);
+
+               fprintf(stderr, "    %s = %s\n", token_names[n], buf);
        }
 }
 
@@ -261,41 +374,35 @@ callback_dumb_increment(struct libwebsocket_context *context,
        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 = user;
+       struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
 
        switch (reason) {
 
        case LWS_CALLBACK_ESTABLISHED:
-               fprintf(stderr, "callback_dumb_increment: "
+               lwsl_info("callback_dumb_increment: "
                                                 "LWS_CALLBACK_ESTABLISHED\n");
                pss->number = 0;
                break;
 
-       /*
-        * in this protocol, we just use the broadcast action as the chance to
-        * send our own connection-specific data and ignore the broadcast info
-        * that is available in the 'in' parameter
-        */
-
-       case LWS_CALLBACK_BROADCAST:
+       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) {
-                       fprintf(stderr, "ERROR %d writing to socket\n", n);
-                       return 1;
+                       lwsl_err("ERROR %d writing to di socket\n", n);
+                       return -1;
                }
                if (close_testing && pss->number == 50) {
-                       fprintf(stderr, "close tesing limit, closing\n");
-                       libwebsocket_close_and_free_session(context, wsi,
-                                                      LWS_CLOSE_STATUS_NORMAL);
+                       lwsl_info("close tesing limit, closing\n");
+                       return -1;
                }
                break;
 
        case LWS_CALLBACK_RECEIVE:
-               fprintf(stderr, "rx %d\n", (int)len);
+//             fprintf(stderr, "rx %d\n", (int)len);
                if (len < 6)
                        break;
-               if (strcmp(in, "reset\n") == 0)
+               if (strcmp((const char *)in, "reset\n") == 0)
                        pss->number = 0;
                break;
        /*
@@ -305,7 +412,7 @@ callback_dumb_increment(struct libwebsocket_context *context,
         */
 
        case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
-               dump_handshake_info((struct lws_tokens *)(long)user);
+               dump_handshake_info(wsi);
                /* you could return non-zero here and kill the connection */
                break;
 
@@ -319,7 +426,7 @@ callback_dumb_increment(struct libwebsocket_context *context,
 
 /* lws-mirror_protocol */
 
-#define MAX_MESSAGE_QUEUE 64
+#define MAX_MESSAGE_QUEUE 128
 
 struct per_session_data__lws_mirror {
        struct libwebsocket *wsi;
@@ -334,6 +441,8 @@ struct a_message {
 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,
@@ -342,21 +451,28 @@ callback_lws_mirror(struct libwebsocket_context *context,
                                               void *user, void *in, size_t len)
 {
        int n;
-       struct per_session_data__lws_mirror *pss = user;
+       struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
 
        switch (reason) {
 
        case LWS_CALLBACK_ESTABLISHED:
-               fprintf(stderr, "callback_lws_mirror: "
+               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;
-               if (pss->ringbuffer_tail != ringbuffer_head) {
+               while (pss->ringbuffer_tail != ringbuffer_head) {
 
                        n = libwebsocket_write(wsi, (unsigned char *)
                                   ringbuffer[pss->ringbuffer_tail].payload +
@@ -364,8 +480,11 @@ callback_lws_mirror(struct libwebsocket_context *context,
                                   ringbuffer[pss->ringbuffer_tail].len,
                                                                LWS_WRITE_TEXT);
                        if (n < 0) {
-                               fprintf(stderr, "ERROR %d writing to socket\n", n);
-                               exit(1);
+                               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))
@@ -373,23 +492,29 @@ callback_lws_mirror(struct libwebsocket_context *context,
                        else
                                pss->ringbuffer_tail++;
 
-                       if (((ringbuffer_head - pss->ringbuffer_tail) %
-                                 MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15))
-                               libwebsocket_rx_flow_control(wsi, 1);
-
-                       libwebsocket_callback_on_writable(context, wsi);
+                       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_BROADCAST:
-               n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
-               if (n < 0)
-                       fprintf(stderr, "mirror write failed\n");
-               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);
 
@@ -404,13 +529,22 @@ callback_lws_mirror(struct libwebsocket_context *context,
                else
                        ringbuffer_head++;
 
-               if (((ringbuffer_head - pss->ringbuffer_tail) %
-                                 MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10))
+               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
@@ -418,7 +552,7 @@ callback_lws_mirror(struct libwebsocket_context *context,
         */
 
        case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
-               dump_handshake_info((struct lws_tokens *)(long)user);
+               dump_handshake_info(wsi);
                /* you could return non-zero here and kill the connection */
                break;
 
@@ -438,78 +572,90 @@ static struct libwebsocket_protocols protocols[] = {
        {
                "http-only",            /* name */
                callback_http,          /* callback */
-               0                       /* per_session_data_size */
+               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)
+               sizeof(struct per_session_data__lws_mirror),
+               128,
        },
-       {
-               NULL, NULL, 0           /* End of list */
-       }
+       { NULL, NULL, 0, 0 } /* terminator */
 };
 
+void sighandler(int sig)
+{
+       force_exit = 1;
+}
+
 static struct option options[] = {
        { "help",       no_argument,            NULL, 'h' },
        { "debug",      required_argument,      NULL, 'd' },
        { "port",       required_argument,      NULL, 'p' },
        { "ssl",        no_argument,            NULL, 's' },
-       { "killmask",   no_argument,            NULL, 'k' },
        { "interface",  required_argument,      NULL, 'i' },
        { "closetest",  no_argument,            NULL, 'c' },
+#ifndef LWS_NO_DAEMONIZE
+       { "daemonize",  no_argument,            NULL, 'D' },
+#endif
        { NULL, 0, 0, 0 }
 };
 
 int main(int argc, char **argv)
 {
        int n = 0;
-       const char *cert_path =
-                           LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
-       const char *key_path =
-                       LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
-       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
-                                                 LWS_SEND_BUFFER_POST_PADDING];
-       int port = 7681;
        int use_ssl = 0;
        struct libwebsocket_context *context;
        int opts = 0;
        char interface_name[128] = "";
-       const char *interface = NULL;
-#ifdef LWS_NO_FORK
+       const char *iface = NULL;
+#ifndef WIN32
+       int syslog_options = LOG_PID | LOG_PERROR;
+#endif
        unsigned int oldus = 0;
+       struct lws_context_creation_info info;
+
+       int debug_level = 7;
+#ifndef LWS_NO_DAEMONIZE
+       int daemonize = 0;
 #endif
 
-       fprintf(stderr, "libwebsockets test server\n"
-                       "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> "
-                                                   "licensed under LGPL2.1\n");
+       memset(&info, 0, sizeof info);
+       info.port = 7681;
 
        while (n >= 0) {
-               n = getopt_long(argc, argv, "ci:khsp:d:", options, NULL);
+               n = getopt_long(argc, argv, "ci:hsp:d:D", options, NULL);
                if (n < 0)
                        continue;
                switch (n) {
+#ifndef LWS_NO_DAEMONIZE
+               case 'D':
+                       daemonize = 1;
+                       #ifndef WIN32
+                       syslog_options &= ~LOG_PERROR;
+                       #endif
+                       break;
+#endif
                case 'd':
-                       lws_set_log_level(atoi(optarg), NULL);
+                       debug_level = atoi(optarg);
                        break;
                case 's':
                        use_ssl = 1;
                        break;
-               case 'k':
-                       opts = LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK;
-                       break;
                case 'p':
-                       port = atoi(optarg);
+                       info.port = atoi(optarg);
                        break;
                case 'i':
                        strncpy(interface_name, optarg, sizeof interface_name);
                        interface_name[(sizeof interface_name) - 1] = '\0';
-                       interface = interface_name;
+                       iface = interface_name;
                        break;
                case 'c':
                        close_testing = 1;
@@ -525,63 +671,81 @@ int main(int argc, char **argv)
                }
        }
 
-       if (!use_ssl)
-               cert_path = key_path = NULL;
-
-       context = libwebsocket_create_context(port, interface, protocols,
-                               libwebsocket_internal_extensions,
-                               cert_path, key_path, NULL, -1, -1, opts, NULL);
-       if (context == NULL) {
-               fprintf(stderr, "libwebsocket init failed\n");
-               return -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
 
-       buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
+       signal(SIGINT, sighandler);
 
-#ifdef LWS_NO_FORK
+#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
 
-       /*
-        * This example shows how to work with no forked service loop
-        */
+       /* 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");
+#ifdef EXTERNAL_POLL
+       max_poll_elements = getdtablesize();
+       pollfds = malloc(max_poll_elements * sizeof (struct 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);
+               return -1;
+       }
+#endif
+
+       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;
 
-       fprintf(stderr, " Using no-fork service loop\n");
+       context = libwebsocket_create_context(&info);
+       if (context == NULL) {
+               lwsl_err("libwebsocket init failed\n");
+               return -1;
+       }
 
        n = 0;
-       while (n >= 0) {
+       while (n >= 0 && !force_exit) {
                struct timeval tv;
 
                gettimeofday(&tv, NULL);
 
                /*
-                * This broadcasts to all dumb-increment-protocol connections
-                * at 20Hz.
-                *
-                * We're just sending a character 'x', in these examples the
-                * callbacks send their own per-connection content.
-                *
-                * You have to send something with nonzero length to get the
-                * callback actions delivered.
-                *
-                * We take care of pre-and-post padding allocation.
+                * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
+                * live websocket connection using the DUMB_INCREMENT protocol,
+                * as soon as it can take more packets (usually immediately)
                 */
 
                if (((unsigned int)tv.tv_usec - oldus) > 50000) {
-                       libwebsockets_broadcast(
-                                       &protocols[PROTOCOL_DUMB_INCREMENT],
-                                       &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+                       libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
                        oldus = tv.tv_usec;
                }
 
-               /*
-                * This example server does not fork or create a thread for
-                * websocket service, it all runs in this single loop.  So,
-                * we have to give the websockets an opportunity to service
-                * "manually".
-                *
-                * If no socket is needing service, the call below returns
-                * immediately and quickly.  Negative return means we are
-                * in process of closing
-                */
 #ifdef EXTERNAL_POLL
 
                /*
@@ -606,56 +770,30 @@ int main(int argc, char **argv)
                                                                  &pollfds[n]) < 0)
                                                goto done;
 #else
-               n = libwebsocket_service(context, 50);
-#endif
-       }
-
-#else /* !LWS_NO_FORK */
-
-       /*
-        * This example shows how to work with the forked websocket service loop
-        */
-
-       fprintf(stderr, " Using forked service loop\n");
-
-       /*
-        * This forks the websocket service action into a subprocess so we
-        * don't have to take care about it.
-        */
-
-       n = libwebsockets_fork_service_loop(context);
-       if (n < 0) {
-               fprintf(stderr, "Unable to fork service loop %d\n", n);
-               return 1;
-       }
-
-       while (1) {
-
-               usleep(50000);
-
                /*
-                * This broadcasts to all dumb-increment-protocol connections
-                * at 20Hz.
+                * If libwebsockets sockets are all we care about,
+                * you can use this api which takes care of the poll()
+                * and looping through finding who needed service.
                 *
-                * We're just sending a character 'x', in these examples the
-                * callbacks send their own per-connection content.
-                *
-                * You have to send something with nonzero length to get the
-                * callback actions delivered.
-                *
-                * We take care of pre-and-post padding allocation.
+                * If no socket needs service, it'll return anyway after
+                * the number of ms in the second argument.
                 */
 
-               libwebsockets_broadcast(&protocols[PROTOCOL_DUMB_INCREMENT],
-                                       &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
+               n = libwebsocket_service(context, 50);
+#endif
        }
 
-#endif
 #ifdef EXTERNAL_POLL
 done:
 #endif
 
        libwebsocket_context_destroy(context);
 
+       lwsl_notice("libwebsockets-test-server exited cleanly\n");
+
+#ifndef WIN32
+       closelog();
+#endif
+
        return 0;
 }