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