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