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