2 * libwebsockets-test-server - libwebsockets test implementation
4 * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
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.
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.
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,
31 #include "../lib/libwebsockets.h"
33 static int close_testing;
40 int max_poll_elements;
42 struct pollfd *pollfds;
46 #endif /* EXTERNAL_POLL */
50 * This demo server shows how to use libwebsockets for one or more
51 * websocket protocols in the same server
53 * It defines the following websocket protocols:
55 * dumb-increment-protocol: once the socket is opened, an incrementing
56 * ascii string is sent down it every 50ms.
57 * If you send "reset\n" on the websocket, then
58 * the incrementing number is reset to 0.
60 * lws-mirror-protocol: copies any received packet to every connection also
61 * using this protocol, including the sender
68 PROTOCOL_DUMB_INCREMENT,
76 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
79 * We take a strict whitelist approach to stop ../ attacks
87 static const struct serveable whitelist[] = {
88 { "/favicon.ico", "image/x-icon" },
89 { "/libwebsockets.org-logo.png", "image/png" },
91 /* last one is the default served if no match */
92 { "/test.html", "text/html" },
95 /* this protocol server (always the first one) just knows how to do HTTP */
97 static int callback_http(struct libwebsocket_context *context,
98 struct libwebsocket *wsi,
99 enum libwebsocket_callback_reasons reason, void *user,
100 void *in, size_t len)
103 char client_name[128];
110 int fd = (int)(long)user;
114 case LWS_CALLBACK_HTTP:
116 for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
117 if (in && strcmp(in, whitelist[n].urlpath) == 0)
120 sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath);
122 if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
123 lwsl_err("Failed to send HTTP file\n");
126 * notice that the sending of the file completes asynchronously,
127 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
133 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
134 // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
135 /* kill the connection after we sent one file */
139 * callback for confirming to continue with client IP appear in
140 * protocol 0 callback since no websocket protocol has been agreed
141 * yet. You can just ignore this if you won't filter on client IP
142 * since the default uhandled callback return is 0 meaning let the
143 * connection continue.
146 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
148 libwebsockets_get_peer_addresses((int)(long)user, client_name,
149 sizeof(client_name), client_ip, sizeof(client_ip));
151 fprintf(stderr, "Received network connect from %s (%s)\n",
152 client_name, client_ip);
154 /* if we returned non-zero from here, we kill the connection */
159 * callbacks for managing the external poll() array appear in
160 * protocol 0 callback
163 case LWS_CALLBACK_ADD_POLL_FD:
165 if (count_pollfds >= max_poll_elements) {
166 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
170 fd_lookup[fd] = count_pollfds;
171 pollfds[count_pollfds].fd = fd;
172 pollfds[count_pollfds].events = (int)(long)len;
173 pollfds[count_pollfds++].revents = 0;
176 case LWS_CALLBACK_DEL_POLL_FD:
177 if (!--count_pollfds)
180 /* have the last guy take up the vacant slot */
181 pollfds[m] = pollfds[count_pollfds];
182 fd_lookup[pollfds[count_pollfds].fd] = m;
185 case LWS_CALLBACK_SET_MODE_POLL_FD:
186 pollfds[fd_lookup[fd]].events |= (int)(long)len;
189 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
190 pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
202 * this is just an example of parsing handshake headers, you don't need this
203 * in your code unless you will filter allowing connections by the header
208 dump_handshake_info(struct lws_tokens *lwst)
211 static const char *token_names[WSI_TOKEN_COUNT] = {
212 /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
213 /*[WSI_TOKEN_HOST] =*/ "Host",
214 /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
215 /*[WSI_TOKEN_KEY1] =*/ "key 1",
216 /*[WSI_TOKEN_KEY2] =*/ "key 2",
217 /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
218 /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
219 /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
220 /*[WSI_TOKEN_DRAFT] =*/ "Draft",
221 /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
224 /*[WSI_TOKEN_KEY] =*/ "Key",
225 /*[WSI_TOKEN_VERSION] =*/ "Version",
226 /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
229 /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
231 /* client receives these */
232 /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
233 /*[WSI_TOKEN_NONCE] =*/ "Nonce",
234 /*[WSI_TOKEN_HTTP] =*/ "Http",
235 /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
238 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
239 if (lwst[n].token == NULL)
242 fprintf(stderr, " %s = %s\n", token_names[n], lwst[n].token);
246 /* dumb_increment protocol */
249 * one of these is auto-created for each connection and a pointer to the
250 * appropriate instance is passed to the callback in the user parameter
252 * for this example protocol we use it to individualize the count for each
256 struct per_session_data__dumb_increment {
261 callback_dumb_increment(struct libwebsocket_context *context,
262 struct libwebsocket *wsi,
263 enum libwebsocket_callback_reasons reason,
264 void *user, void *in, size_t len)
267 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
268 LWS_SEND_BUFFER_POST_PADDING];
269 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
270 struct per_session_data__dumb_increment *pss = user;
274 case LWS_CALLBACK_ESTABLISHED:
275 lwsl_info("callback_dumb_increment: "
276 "LWS_CALLBACK_ESTABLISHED\n");
281 * in this protocol, we just use the broadcast action as the chance to
282 * send our own connection-specific data and ignore the broadcast info
283 * that is available in the 'in' parameter
286 case LWS_CALLBACK_BROADCAST:
287 n = sprintf((char *)p, "%d", pss->number++);
288 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
290 lwsl_err("ERROR %d writing to socket\n", n);
293 if (close_testing && pss->number == 50) {
294 lwsl_info("close tesing limit, closing\n");
295 libwebsocket_close_and_free_session(context, wsi,
296 LWS_CLOSE_STATUS_NORMAL);
300 case LWS_CALLBACK_RECEIVE:
301 // fprintf(stderr, "rx %d\n", (int)len);
304 if (strcmp(in, "reset\n") == 0)
308 * this just demonstrates how to use the protocol filter. If you won't
309 * study and reject connections based on header content, you don't need
310 * to handle this callback
313 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
314 dump_handshake_info((struct lws_tokens *)(long)user);
315 /* you could return non-zero here and kill the connection */
326 /* lws-mirror_protocol */
328 #define MAX_MESSAGE_QUEUE 128
330 struct per_session_data__lws_mirror {
331 struct libwebsocket *wsi;
340 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
341 static int ringbuffer_head;
343 static struct libwebsocket *wsi_choked[20];
344 static int num_wsi_choked;
347 callback_lws_mirror(struct libwebsocket_context *context,
348 struct libwebsocket *wsi,
349 enum libwebsocket_callback_reasons reason,
350 void *user, void *in, size_t len)
353 struct per_session_data__lws_mirror *pss = user;
357 case LWS_CALLBACK_ESTABLISHED:
358 lwsl_info("callback_lws_mirror: "
359 "LWS_CALLBACK_ESTABLISHED\n");
360 pss->ringbuffer_tail = ringbuffer_head;
364 case LWS_CALLBACK_SERVER_WRITEABLE:
367 while (pss->ringbuffer_tail != ringbuffer_head) {
369 n = libwebsocket_write(wsi, (unsigned char *)
370 ringbuffer[pss->ringbuffer_tail].payload +
371 LWS_SEND_BUFFER_PRE_PADDING,
372 ringbuffer[pss->ringbuffer_tail].len,
375 lwsl_err("ERROR %d writing to socket\n", n);
379 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
380 pss->ringbuffer_tail = 0;
382 pss->ringbuffer_tail++;
384 if (((ringbuffer_head - pss->ringbuffer_tail) &
385 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) {
386 for (n = 0; n < num_wsi_choked; n++)
387 libwebsocket_rx_flow_control(wsi_choked[n], 1);
390 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
392 if (lws_send_pipe_choked(wsi)) {
393 libwebsocket_callback_on_writable(context, wsi);
399 case LWS_CALLBACK_BROADCAST:
400 n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT);
402 lwsl_err("mirror write failed\n");
405 case LWS_CALLBACK_RECEIVE:
407 if (((ringbuffer_head - pss->ringbuffer_tail) &
408 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
409 lwsl_err("dropping!\n");
413 if (ringbuffer[ringbuffer_head].payload)
414 free(ringbuffer[ringbuffer_head].payload);
416 ringbuffer[ringbuffer_head].payload =
417 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
418 LWS_SEND_BUFFER_POST_PADDING);
419 ringbuffer[ringbuffer_head].len = len;
420 memcpy((char *)ringbuffer[ringbuffer_head].payload +
421 LWS_SEND_BUFFER_PRE_PADDING, in, len);
422 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
427 if (((ringbuffer_head - pss->ringbuffer_tail) &
428 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
432 if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) {
433 libwebsocket_rx_flow_control(wsi, 0);
434 wsi_choked[num_wsi_choked++] = wsi;
437 // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
439 libwebsocket_callback_on_writable_all_protocol(
440 libwebsockets_get_protocol(wsi));
444 * this just demonstrates how to use the protocol filter. If you won't
445 * study and reject connections based on header content, you don't need
446 * to handle this callback
449 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
450 dump_handshake_info((struct lws_tokens *)(long)user);
451 /* you could return non-zero here and kill the connection */
462 /* list of supported protocols and callbacks */
464 static struct libwebsocket_protocols protocols[] = {
465 /* first protocol must always be HTTP handler */
468 "http-only", /* name */
469 callback_http, /* callback */
470 0 /* per_session_data_size */
473 "dumb-increment-protocol",
474 callback_dumb_increment,
475 sizeof(struct per_session_data__dumb_increment),
478 "lws-mirror-protocol",
480 sizeof(struct per_session_data__lws_mirror)
483 NULL, NULL, 0 /* End of list */
487 static struct option options[] = {
488 { "help", no_argument, NULL, 'h' },
489 { "debug", required_argument, NULL, 'd' },
490 { "port", required_argument, NULL, 'p' },
491 { "ssl", no_argument, NULL, 's' },
492 { "interface", required_argument, NULL, 'i' },
493 { "closetest", no_argument, NULL, 'c' },
494 #ifndef LWS_NO_DAEMONIZE
495 { "daemonize", no_argument, NULL, 'D' },
500 int main(int argc, char **argv)
503 const char *cert_path =
504 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
505 const char *key_path =
506 LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
507 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1024 +
508 LWS_SEND_BUFFER_POST_PADDING];
511 struct libwebsocket_context *context;
513 char interface_name[128] = "";
514 const char *interface = NULL;
515 int syslog_options = LOG_PID | LOG_PERROR;
517 unsigned int oldus = 0;
520 #ifndef LWS_NO_DAEMONIZE
525 n = getopt_long(argc, argv, "ci:hsp:d:D", options, NULL);
529 #ifndef LWS_NO_DAEMONIZE
532 syslog_options &= ~LOG_PERROR;
536 debug_level = atoi(optarg);
545 strncpy(interface_name, optarg, sizeof interface_name);
546 interface_name[(sizeof interface_name) - 1] = '\0';
547 interface = interface_name;
551 fprintf(stderr, " Close testing mode -- closes on "
552 "client after 50 dumb increments"
553 "and suppresses lws_mirror spam\n");
556 fprintf(stderr, "Usage: test-server "
557 "[--port=<p>] [--ssl] "
558 "[-d <log bitfield>]\n");
563 #ifndef LWS_NO_DAEMONIZE
565 * normally lock path would be /var/lock/lwsts or similar, to
566 * simplify getting started without having to take care about
567 * permissions or running as root, set to /tmp/.lwsts-lock
569 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
570 fprintf(stderr, "Failed to daemonize\n");
574 /* we will only try to log things according to our debug_level */
575 setlogmask(LOG_UPTO (LOG_DEBUG));
576 openlog("lwsts", syslog_options, LOG_DAEMON);
578 /* tell the library what debug level to emit and to send it to syslog */
579 lws_set_log_level(debug_level, lwsl_emit_syslog);
581 lwsl_notice("libwebsockets test server - "
582 "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
583 "licensed under LGPL2.1\n");
585 cert_path = key_path = NULL;
587 max_poll_elements = getdtablesize();
588 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
589 fd_lookup = malloc(max_poll_elements * sizeof (int));
590 if (pollfds == NULL || fd_lookup == NULL) {
591 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
596 context = libwebsocket_create_context(port, interface, protocols,
597 #ifndef LWS_NO_EXTENSIONS
598 libwebsocket_internal_extensions,
602 cert_path, key_path, NULL, -1, -1, opts, NULL);
603 if (context == NULL) {
604 lwsl_err("libwebsocket init failed\n");
608 buf[LWS_SEND_BUFFER_PRE_PADDING] = 'x';
613 * This example shows how to work with no forked service loop
616 lwsl_info(" Using no-fork service loop\n");
622 gettimeofday(&tv, NULL);
625 * This broadcasts to all dumb-increment-protocol connections
628 * We're just sending a character 'x', in these examples the
629 * callbacks send their own per-connection content.
631 * You have to send something with nonzero length to get the
632 * callback actions delivered.
634 * We take care of pre-and-post padding allocation.
637 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
638 libwebsockets_broadcast(
639 &protocols[PROTOCOL_DUMB_INCREMENT],
640 &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
645 * This example server does not fork or create a thread for
646 * websocket service, it all runs in this single loop. So,
647 * we have to give the websockets an opportunity to service
650 * If no socket is needing service, the call below returns
651 * immediately and quickly. Negative return means we are
652 * in process of closing
657 * this represents an existing server's single poll action
658 * which also includes libwebsocket sockets
661 n = poll(pollfds, count_pollfds, 50);
667 for (n = 0; n < count_pollfds; n++)
668 if (pollfds[n].revents)
670 * returns immediately if the fd does not
671 * match anything under libwebsockets
674 if (libwebsocket_service_fd(context,
678 n = libwebsocket_service(context, 50);
682 #else /* !LWS_NO_FORK */
685 * This example shows how to work with the forked websocket service loop
688 lwsl_info(" Using forked service loop\n");
691 * This forks the websocket service action into a subprocess so we
692 * don't have to take care about it.
695 n = libwebsockets_fork_service_loop(context);
697 lwsl_err("Unable to fork service loop %d\n", n);
708 * This broadcasts to all dumb-increment-protocol connections
711 * We're just sending a character 'x', in these examples the
712 * callbacks send their own per-connection content.
714 * You have to send something with nonzero length to get the
715 * callback actions delivered.
717 * We take care of pre-and-post padding allocation.
720 libwebsockets_broadcast_foreign(&protocols[PROTOCOL_DUMB_INCREMENT],
721 &buf[LWS_SEND_BUFFER_PRE_PADDING], 1);
729 libwebsocket_context_destroy(context);
731 lwsl_notice("libwebsockets-test-server exited cleanly\n");