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