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