autocreate foreign broadcast sockets on broadcast
[profile/ivi/libwebsockets.git] / test-server / test-server.c
1 /*
2  * libwebsockets-test-server - libwebsockets test implementation
3  *
4  * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <string.h>
27 #include <sys/time.h>
28 #include <assert.h>
29 #include <syslog.h>
30
31 #include "../lib/libwebsockets.h"
32
33 static int close_testing;
34
35 #ifdef EXTERNAL_POLL
36 #ifndef LWS_NO_FORK
37 #define LWS_NO_FORK
38 #endif
39
40 int max_poll_elements;
41
42 struct pollfd *pollfds;
43 int *fd_lookup;
44 int count_pollfds;
45
46 #endif /* EXTERNAL_POLL */
47
48
49 /*
50  * This demo server shows how to use libwebsockets for one or more
51  * websocket protocols in the same server
52  *
53  * It defines the following websocket protocols:
54  *
55  *  dumb-increment-protocol:  once the socket is opened, an incrementing
56  *                              ascii string is sent down it every 50ms.
57  *                              If you send "reset\n" on the websocket, then
58  *                              the incrementing number is reset to 0.
59  *
60  *  lws-mirror-protocol: copies any received packet to every connection also
61  *                              using this protocol, including the sender
62  */
63
64 enum demo_protocols {
65         /* always first */
66         PROTOCOL_HTTP = 0,
67
68         PROTOCOL_DUMB_INCREMENT,
69         PROTOCOL_LWS_MIRROR,
70
71         /* always last */
72         DEMO_PROTOCOL_COUNT
73 };
74
75
76 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
77
78 /*
79  * We take a strict whitelist approach to stop ../ attacks
80  */
81
82 struct serveable {
83         const char *urlpath;
84         const char *mimetype;
85 }; 
86
87 static const struct serveable whitelist[] = {
88         { "/favicon.ico", "image/x-icon" },
89         { "/libwebsockets.org-logo.png", "image/png" },
90
91         /* last one is the default served if no match */
92         { "/test.html", "text/html" },
93 };
94
95 /* this protocol server (always the first one) just knows how to do HTTP */
96
97 static int callback_http(struct libwebsocket_context *context,
98                 struct libwebsocket *wsi,
99                 enum libwebsocket_callback_reasons reason, void *user,
100                                                            void *in, size_t len)
101 {
102 #if 0
103         char client_name[128];
104         char client_ip[128];
105 #endif
106         char buf[256];
107         int n;
108 #ifdef EXTERNAL_POLL
109         int m;
110         int fd = (int)(long)user;
111 #endif
112
113         switch (reason) {
114         case LWS_CALLBACK_HTTP:
115
116                 for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
117                         if (in && strcmp(in, whitelist[n].urlpath) == 0)
118                                 break;
119
120                 sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath);
121
122                 if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
123                         lwsl_err("Failed to send HTTP file\n");
124
125                 /*
126                  * notice that the sending of the file completes asynchronously,
127                  * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
128                  * it's done
129                  */
130
131                 break;
132
133         case LWS_CALLBACK_HTTP_FILE_COMPLETION:
134 //              lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
135                 /* kill the connection after we sent one file */
136                 return 1;
137
138         /*
139          * callback for confirming to continue with client IP appear in
140          * protocol 0 callback since no websocket protocol has been agreed
141          * yet.  You can just ignore this if you won't filter on client IP
142          * since the default uhandled callback return is 0 meaning let the
143          * connection continue.
144          */
145
146         case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
147 #if 0
148                 libwebsockets_get_peer_addresses((int)(long)user, client_name,
149                              sizeof(client_name), client_ip, sizeof(client_ip));
150
151                 fprintf(stderr, "Received network connect from %s (%s)\n",
152                                                         client_name, client_ip);
153 #endif
154                 /* if we returned non-zero from here, we kill the connection */
155                 break;
156
157 #ifdef EXTERNAL_POLL
158         /*
159          * callbacks for managing the external poll() array appear in
160          * protocol 0 callback
161          */
162
163         case LWS_CALLBACK_ADD_POLL_FD:
164
165                 if (count_pollfds >= max_poll_elements) {
166                         lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
167                         return 1;
168                 }
169
170                 fd_lookup[fd] = count_pollfds;
171                 pollfds[count_pollfds].fd = fd;
172                 pollfds[count_pollfds].events = (int)(long)len;
173                 pollfds[count_pollfds++].revents = 0;
174                 break;
175
176         case LWS_CALLBACK_DEL_POLL_FD:
177                 if (!--count_pollfds)
178                         break;
179                 m = fd_lookup[fd];
180                 /* have the last guy take up the vacant slot */
181                 pollfds[m] = pollfds[count_pollfds];
182                 fd_lookup[pollfds[count_pollfds].fd] = m;
183                 break;
184
185         case LWS_CALLBACK_SET_MODE_POLL_FD:
186                 pollfds[fd_lookup[fd]].events |= (int)(long)len;
187                 break;
188
189         case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
190                 pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
191                 break;
192 #endif
193
194         default:
195                 break;
196         }
197
198         return 0;
199 }
200
201 /*
202  * this is just an example of parsing handshake headers, you don't need this
203  * in your code unless you will filter allowing connections by the header
204  * content
205  */
206
207 static void
208 dump_handshake_info(struct lws_tokens *lwst)
209 {
210         int n;
211         static const char *token_names[WSI_TOKEN_COUNT] = {
212                 /*[WSI_TOKEN_GET_URI]           =*/ "GET URI",
213                 /*[WSI_TOKEN_HOST]              =*/ "Host",
214                 /*[WSI_TOKEN_CONNECTION]        =*/ "Connection",
215                 /*[WSI_TOKEN_KEY1]              =*/ "key 1",
216                 /*[WSI_TOKEN_KEY2]              =*/ "key 2",
217                 /*[WSI_TOKEN_PROTOCOL]          =*/ "Protocol",
218                 /*[WSI_TOKEN_UPGRADE]           =*/ "Upgrade",
219                 /*[WSI_TOKEN_ORIGIN]            =*/ "Origin",
220                 /*[WSI_TOKEN_DRAFT]             =*/ "Draft",
221                 /*[WSI_TOKEN_CHALLENGE]         =*/ "Challenge",
222
223                 /* new for 04 */
224                 /*[WSI_TOKEN_KEY]               =*/ "Key",
225                 /*[WSI_TOKEN_VERSION]           =*/ "Version",
226                 /*[WSI_TOKEN_SWORIGIN]          =*/ "Sworigin",
227
228                 /* new for 05 */
229                 /*[WSI_TOKEN_EXTENSIONS]        =*/ "Extensions",
230
231                 /* client receives these */
232                 /*[WSI_TOKEN_ACCEPT]            =*/ "Accept",
233                 /*[WSI_TOKEN_NONCE]             =*/ "Nonce",
234                 /*[WSI_TOKEN_HTTP]              =*/ "Http",
235                 /*[WSI_TOKEN_MUXURL]    =*/ "MuxURL",
236         };
237
238         for (n = 0; n < WSI_TOKEN_COUNT; n++) {
239                 if (lwst[n].token == NULL)
240                         continue;
241
242                 fprintf(stderr, "    %s = %s\n", token_names[n], lwst[n].token);
243         }
244 }
245
246 /* dumb_increment protocol */
247
248 /*
249  * one of these is auto-created for each connection and a pointer to the
250  * appropriate instance is passed to the callback in the user parameter
251  *
252  * for this example protocol we use it to individualize the count for each
253  * connection.
254  */
255
256 struct per_session_data__dumb_increment {
257         int number;
258 };
259
260 static int
261 callback_dumb_increment(struct libwebsocket_context *context,
262                         struct libwebsocket *wsi,
263                         enum libwebsocket_callback_reasons reason,
264                                                void *user, void *in, size_t len)
265 {
266         int n;
267         unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
268                                                   LWS_SEND_BUFFER_POST_PADDING];
269         unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
270         struct per_session_data__dumb_increment *pss = user;
271
272         switch (reason) {
273
274         case LWS_CALLBACK_ESTABLISHED:
275                 lwsl_info("callback_dumb_increment: "
276                                                  "LWS_CALLBACK_ESTABLISHED\n");
277                 pss->number = 0;
278                 break;
279
280         /*
281          * in this protocol, we just use the broadcast action as the chance to
282          * send our own connection-specific data and ignore the broadcast info
283          * that is available in the 'in' parameter
284          */
285
286         case LWS_CALLBACK_BROADCAST:
287                 n = sprintf((char *)p, "%d", pss->number++);
288                 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
289                 if (n < 0) {
290                         lwsl_err("ERROR %d writing to socket\n", n);
291                         return 1;
292                 }
293                 if (close_testing && pss->number == 50) {
294                         lwsl_info("close tesing limit, closing\n");
295                         libwebsocket_close_and_free_session(context, wsi,
296                                                        LWS_CLOSE_STATUS_NORMAL);
297                 }
298                 break;
299
300         case LWS_CALLBACK_RECEIVE:
301 //              fprintf(stderr, "rx %d\n", (int)len);
302                 if (len < 6)
303                         break;
304                 if (strcmp(in, "reset\n") == 0)
305                         pss->number = 0;
306                 break;
307         /*
308          * this just demonstrates how to use the protocol filter. If you won't
309          * study and reject connections based on header content, you don't need
310          * to handle this callback
311          */
312
313         case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
314                 dump_handshake_info((struct lws_tokens *)(long)user);
315                 /* you could return non-zero here and kill the connection */
316                 break;
317
318         default:
319                 break;
320         }
321
322         return 0;
323 }
324
325
326 /* lws-mirror_protocol */
327
328 #define MAX_MESSAGE_QUEUE 128
329
330 struct per_session_data__lws_mirror {
331         struct libwebsocket *wsi;
332         int ringbuffer_tail;
333 };
334
335 struct a_message {
336         void *payload;
337         size_t len;
338 };
339
340 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
341 static int ringbuffer_head;
342
343 static struct libwebsocket *wsi_choked[20];
344 static int num_wsi_choked;
345
346 static int
347 callback_lws_mirror(struct libwebsocket_context *context,
348                         struct libwebsocket *wsi,
349                         enum libwebsocket_callback_reasons reason,
350                                                void *user, void *in, size_t len)
351 {
352         int n;
353         struct per_session_data__lws_mirror *pss = user;
354
355         switch (reason) {
356
357         case LWS_CALLBACK_ESTABLISHED:
358                 lwsl_info("callback_lws_mirror: "
359                                                  "LWS_CALLBACK_ESTABLISHED\n");
360                 pss->ringbuffer_tail = ringbuffer_head;
361                 pss->wsi = wsi;
362                 break;
363
364         case LWS_CALLBACK_SERVER_WRITEABLE:
365                 if (close_testing)
366                         break;
367                 while (pss->ringbuffer_tail != ringbuffer_head) {
368
369                         n = libwebsocket_write(wsi, (unsigned char *)
370                                    ringbuffer[pss->ringbuffer_tail].payload +
371                                    LWS_SEND_BUFFER_PRE_PADDING,
372                                    ringbuffer[pss->ringbuffer_tail].len,
373                                                                 LWS_WRITE_TEXT);
374                         if (n < 0) {
375                                 lwsl_err("ERROR %d writing to socket\n", n);
376                                 return 1;
377                         }
378
379                         if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
380                                 pss->ringbuffer_tail = 0;
381                         else
382                                 pss->ringbuffer_tail++;
383
384                         if (((ringbuffer_head - pss->ringbuffer_tail) &
385                                   (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) {
386                                 for (n = 0; n < num_wsi_choked; n++)
387                                         libwebsocket_rx_flow_control(wsi_choked[n], 1);
388                                 num_wsi_choked = 0;
389                         }
390                         // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
391
392                         if (lws_send_pipe_choked(wsi)) {
393                                 libwebsocket_callback_on_writable(context, wsi);
394                                 return 0;
395                         }
396                 }
397                 break;
398
399         case LWS_CALLBACK_BROADCAST:
400                 n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
401                 if (n < 0)
402                         lwsl_err("mirror write failed\n");
403                 break;
404
405         case LWS_CALLBACK_RECEIVE:
406
407                 if (((ringbuffer_head - pss->ringbuffer_tail) &
408                                   (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
409                         lwsl_err("dropping!\n");
410                         goto choke;
411                 }
412
413                 if (ringbuffer[ringbuffer_head].payload)
414                         free(ringbuffer[ringbuffer_head].payload);
415
416                 ringbuffer[ringbuffer_head].payload =
417                                 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
418                                                   LWS_SEND_BUFFER_POST_PADDING);
419                 ringbuffer[ringbuffer_head].len = len;
420                 memcpy((char *)ringbuffer[ringbuffer_head].payload +
421                                           LWS_SEND_BUFFER_PRE_PADDING, in, len);
422                 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
423                         ringbuffer_head = 0;
424                 else
425                         ringbuffer_head++;
426
427                 if (((ringbuffer_head - pss->ringbuffer_tail) &
428                                   (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
429                         goto done;
430
431 choke:
432                 if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) {
433                         libwebsocket_rx_flow_control(wsi, 0);
434                         wsi_choked[num_wsi_choked++] = wsi;
435                 }
436
437 //              lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
438 done:
439                 libwebsocket_callback_on_writable_all_protocol(
440                                                libwebsockets_get_protocol(wsi));
441                 break;
442
443         /*
444          * this just demonstrates how to use the protocol filter. If you won't
445          * study and reject connections based on header content, you don't need
446          * to handle this callback
447          */
448
449         case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
450                 dump_handshake_info((struct lws_tokens *)(long)user);
451                 /* you could return non-zero here and kill the connection */
452                 break;
453
454         default:
455                 break;
456         }
457
458         return 0;
459 }
460
461
462 /* list of supported protocols and callbacks */
463
464 static struct libwebsocket_protocols protocols[] = {
465         /* first protocol must always be HTTP handler */
466
467         {
468                 "http-only",            /* name */
469                 callback_http,          /* callback */
470                 0                       /* per_session_data_size */
471         },
472         {
473                 "dumb-increment-protocol",
474                 callback_dumb_increment,
475                 sizeof(struct per_session_data__dumb_increment),
476         },
477         {
478                 "lws-mirror-protocol",
479                 callback_lws_mirror,
480                 sizeof(struct per_session_data__lws_mirror)
481         },
482         {
483                 NULL, NULL, 0           /* End of list */
484         }
485 };
486
487 static struct option options[] = {
488         { "help",       no_argument,            NULL, 'h' },
489         { "debug",      required_argument,      NULL, 'd' },
490         { "port",       required_argument,      NULL, 'p' },
491         { "ssl",        no_argument,            NULL, 's' },
492         { "interface",  required_argument,      NULL, 'i' },
493         { "closetest",  no_argument,            NULL, 'c' },
494 #ifndef LWS_NO_DAEMONIZE
495         { "daemonize",  no_argument,            NULL, 'D' },
496 #endif
497         { NULL, 0, 0, 0 }
498 };
499
500 int main(int argc, char **argv)
501 {
502         int n = 0;
503         const char *cert_path =
504                             LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
505         const char *key_path =
506                         LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
507         unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
508                                                   LWS_SEND_BUFFER_POST_PADDING];
509         int port = 7681;
510         int use_ssl = 0;
511         struct libwebsocket_context *context;
512         int opts = 0;
513         char interface_name[128] = "";
514         const char *interface = NULL;
515         int syslog_options = LOG_PID | LOG_PERROR;
516 #ifdef LWS_NO_FORK
517         unsigned int oldus = 0;
518 #endif
519         int debug_level = 7;
520 #ifndef LWS_NO_DAEMONIZE
521         int daemonize = 0;
522 #endif
523
524         while (n >= 0) {
525                 n = getopt_long(argc, argv, "ci:hsp:d:D", options, NULL);
526                 if (n < 0)
527                         continue;
528                 switch (n) {
529 #ifndef LWS_NO_DAEMONIZE
530                 case 'D':
531                         daemonize = 1;
532                         syslog_options &= ~LOG_PERROR;
533                         break;
534 #endif
535                 case 'd':
536                         debug_level = atoi(optarg);
537                         break;
538                 case 's':
539                         use_ssl = 1;
540                         break;
541                 case 'p':
542                         port = atoi(optarg);
543                         break;
544                 case 'i':
545                         strncpy(interface_name, optarg, sizeof interface_name);
546                         interface_name[(sizeof interface_name) - 1] = '\0';
547                         interface = interface_name;
548                         break;
549                 case 'c':
550                         close_testing = 1;
551                         fprintf(stderr, " Close testing mode -- closes on "
552                                            "client after 50 dumb increments"
553                                            "and suppresses lws_mirror spam\n");
554                         break;
555                 case 'h':
556                         fprintf(stderr, "Usage: test-server "
557                                         "[--port=<p>] [--ssl] "
558                                         "[-d <log bitfield>]\n");
559                         exit(1);
560                 }
561         }
562
563 #ifndef LWS_NO_DAEMONIZE
564         /* 
565          * normally lock path would be /var/lock/lwsts or similar, to
566          * simplify getting started without having to take care about
567          * permissions or running as root, set to /tmp/.lwsts-lock
568          */
569         if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
570                 fprintf(stderr, "Failed to daemonize\n");
571                 return 1;
572         }
573 #endif
574         /* we will only try to log things according to our debug_level */
575         setlogmask(LOG_UPTO (LOG_DEBUG));
576         openlog("lwsts", syslog_options, LOG_DAEMON);
577
578         /* tell the library what debug level to emit and to send it to syslog */
579         lws_set_log_level(debug_level, lwsl_emit_syslog);
580
581         lwsl_notice("libwebsockets test server - "
582                         "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
583                                                     "licensed under LGPL2.1\n");
584         if (!use_ssl)
585                 cert_path = key_path = NULL;
586 #ifdef EXTERNAL_POLL
587         max_poll_elements = getdtablesize();
588         pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
589         fd_lookup = malloc(max_poll_elements * sizeof (int));
590         if (pollfds == NULL || fd_lookup == NULL) {
591                 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
592                 return -1;
593         }
594 #endif
595
596         context = libwebsocket_create_context(port, interface, protocols,
597 #ifndef LWS_NO_EXTENSIONS
598                                 libwebsocket_internal_extensions,
599 #else
600                                 NULL,
601 #endif
602                                 cert_path, key_path, NULL, -1, -1, opts, NULL);
603         if (context == NULL) {
604                 lwsl_err("libwebsocket init failed\n");
605                 return -1;
606         }
607
608         buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
609
610 #ifdef LWS_NO_FORK
611
612         /*
613          * This example shows how to work with no forked service loop
614          */
615
616         lwsl_info(" Using no-fork service loop\n");
617
618         n = 0;
619         while (n >= 0) {
620                 struct timeval tv;
621
622                 gettimeofday(&tv, NULL);
623
624                 /*
625                  * This broadcasts to all dumb-increment-protocol connections
626                  * at 20Hz.
627                  *
628                  * We're just sending a character 'x', in these examples the
629                  * callbacks send their own per-connection content.
630                  *
631                  * You have to send something with nonzero length to get the
632                  * callback actions delivered.
633                  *
634                  * We take care of pre-and-post padding allocation.
635                  */
636
637                 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
638                         libwebsockets_broadcast(
639                                         &protocols[PROTOCOL_DUMB_INCREMENT],
640                                         &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
641                         oldus = tv.tv_usec;
642                 }
643
644                 /*
645                  * This example server does not fork or create a thread for
646                  * websocket service, it all runs in this single loop.  So,
647                  * we have to give the websockets an opportunity to service
648                  * "manually".
649                  *
650                  * If no socket is needing service, the call below returns
651                  * immediately and quickly.  Negative return means we are
652                  * in process of closing
653                  */
654 #ifdef EXTERNAL_POLL
655
656                 /*
657                  * this represents an existing server's single poll action
658                  * which also includes libwebsocket sockets
659                  */
660
661                 n = poll(pollfds, count_pollfds, 50);
662                 if (n < 0)
663                         continue;
664
665
666                 if (n)
667                         for (n = 0; n < count_pollfds; n++)
668                                 if (pollfds[n].revents)
669                                         /*
670                                         * returns immediately if the fd does not
671                                         * match anything under libwebsockets
672                                         * control
673                                         */
674                                         if (libwebsocket_service_fd(context,
675                                                                   &pollfds[n]) < 0)
676                                                 goto done;
677 #else
678                 n = libwebsocket_service(context, 50);
679 #endif
680         }
681
682 #else /* !LWS_NO_FORK */
683
684         /*
685          * This example shows how to work with the forked websocket service loop
686          */
687
688         lwsl_info(" Using forked service loop\n");
689
690         /*
691          * This forks the websocket service action into a subprocess so we
692          * don't have to take care about it.
693          */
694
695         n = libwebsockets_fork_service_loop(context);
696         if (n < 0) {
697                 lwsl_err("Unable to fork service loop %d\n", n);
698                 return 1;
699         }
700         if (n)
701                 exit(0);
702
703         while (1) {
704
705                 usleep(50000);
706
707                 /*
708                  * This broadcasts to all dumb-increment-protocol connections
709                  * at 20Hz.
710                  *
711                  * We're just sending a character 'x', in these examples the
712                  * callbacks send their own per-connection content.
713                  *
714                  * You have to send something with nonzero length to get the
715                  * callback actions delivered.
716                  *
717                  * We take care of pre-and-post padding allocation.
718                  */
719
720                 libwebsockets_broadcast_foreign(&protocols[PROTOCOL_DUMB_INCREMENT],
721                                         &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
722         }
723
724 #endif
725 #ifdef EXTERNAL_POLL
726 done:
727 #endif
728
729         libwebsocket_context_destroy(context);
730
731         lwsl_notice("libwebsockets-test-server exited cleanly\n");
732
733         closelog();
734
735         return 0;
736 }