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,
22 #include "lws_config.h"
45 #include "../lib/libwebsockets.h"
47 static int close_testing;
48 int max_poll_elements;
50 struct pollfd *pollfds;
53 static volatile int force_exit = 0;
54 static struct libwebsocket_context *context;
57 * This demo server shows how to use libwebsockets for one or more
58 * websocket protocols in the same server
60 * It defines the following websocket protocols:
62 * dumb-increment-protocol: once the socket is opened, an incrementing
63 * ascii string is sent down it every 50ms.
64 * If you send "reset\n" on the websocket, then
65 * the incrementing number is reset to 0.
67 * lws-mirror-protocol: copies any received packet to every connection also
68 * using this protocol, including the sender
75 PROTOCOL_DUMB_INCREMENT,
83 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
84 char *resource_path = LOCAL_RESOURCE_PATH;
87 * We take a strict whitelist approach to stop ../ attacks
95 struct per_session_data__http {
100 * this is just an example of parsing handshake headers, you don't need this
101 * in your code unless you will filter allowing connections by the header
106 dump_handshake_info(struct libwebsocket *wsi)
109 static const char *token_names[] = {
110 /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
111 /*[WSI_TOKEN_POST_URI] =*/ "POST URI",
112 /*[WSI_TOKEN_HOST] =*/ "Host",
113 /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
114 /*[WSI_TOKEN_KEY1] =*/ "key 1",
115 /*[WSI_TOKEN_KEY2] =*/ "key 2",
116 /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
117 /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
118 /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
119 /*[WSI_TOKEN_DRAFT] =*/ "Draft",
120 /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
123 /*[WSI_TOKEN_KEY] =*/ "Key",
124 /*[WSI_TOKEN_VERSION] =*/ "Version",
125 /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
128 /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
130 /* client receives these */
131 /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
132 /*[WSI_TOKEN_NONCE] =*/ "Nonce",
133 /*[WSI_TOKEN_HTTP] =*/ "Http",
136 "If-Modified-Since:",
150 /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
154 for (n = 0; n < sizeof(token_names) / sizeof(token_names[0]); n++) {
155 if (!lws_hdr_total_length(wsi, n))
158 lws_hdr_copy(wsi, buf, sizeof buf, n);
160 fprintf(stderr, " %s = %s\n", token_names[n], buf);
164 const char * get_mimetype(const char *file)
166 int n = strlen(file);
171 if (!strcmp(&file[n - 4], ".ico"))
172 return "image/x-icon";
174 if (!strcmp(&file[n - 4], ".png"))
177 if (!strcmp(&file[n - 5], ".html"))
183 /* this protocol server (always the first one) just knows how to do HTTP */
185 static int callback_http(struct libwebsocket_context *context,
186 struct libwebsocket *wsi,
187 enum libwebsocket_callback_reasons reason, void *user,
188 void *in, size_t len)
191 char client_name[128];
195 char leaf_path[1024];
201 static unsigned char buffer[4096];
202 struct stat stat_buf;
203 struct per_session_data__http *pss =
204 (struct per_session_data__http *)user;
205 const char *mimetype;
207 struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
211 case LWS_CALLBACK_HTTP:
213 dump_handshake_info(wsi);
216 libwebsockets_return_http_status(context, wsi,
217 HTTP_STATUS_BAD_REQUEST, NULL);
221 /* this server has no concept of directories */
222 if (strchr((const char *)in + 1, '/')) {
223 libwebsockets_return_http_status(context, wsi,
224 HTTP_STATUS_FORBIDDEN, NULL);
228 /* if a legal POST URL, let it continue and accept data */
229 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
232 /* check for the "send a big file by hand" example case */
234 if (!strcmp((const char *)in, "/leaf.jpg")) {
235 if (strlen(resource_path) > sizeof(leaf_path) - 10)
237 sprintf(leaf_path, "%s/leaf.jpg", resource_path);
239 /* well, let's demonstrate how to send the hard way */
244 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
246 pss->fd = open(leaf_path, O_RDONLY);
252 fstat(pss->fd, &stat_buf);
255 * we will send a big jpeg file, but it could be
256 * anything. Set the Content-Type: appropriately
257 * so the browser knows what to do with it.
260 p += sprintf((char *)p,
261 "HTTP/1.0 200 OK\x0d\x0a"
262 "Server: libwebsockets\x0d\x0a"
263 "Content-Type: image/jpeg\x0d\x0a"
264 "Content-Length: %u\x0d\x0a\x0d\x0a",
265 (unsigned int)stat_buf.st_size);
268 * send the http headers...
269 * this won't block since it's the first payload sent
270 * on the connection since it was established
271 * (too small for partial)
274 n = libwebsocket_write(wsi, buffer,
275 p - buffer, LWS_WRITE_HTTP);
282 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
284 libwebsocket_callback_on_writable(context, wsi);
288 /* if not, send a file the easy way */
289 strcpy(buf, resource_path);
290 if (strcmp(in, "/")) {
291 if (*((const char *)in) != '/')
293 strncat(buf, in, sizeof(buf) - strlen(resource_path));
294 } else /* default file to serve */
295 strcat(buf, "/test.html");
296 buf[sizeof(buf) - 1] = '\0';
298 /* refuse to serve files we don't understand */
299 mimetype = get_mimetype(buf);
301 lwsl_err("Unknown mimetype for %s\n", buf);
302 libwebsockets_return_http_status(context, wsi,
303 HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
307 /* demostrates how to set a cookie on / */
309 other_headers = NULL;
310 if (!strcmp((const char *)in, "/") &&
311 !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
312 /* this isn't very unguessable but it'll do for us */
313 gettimeofday(&tv, NULL);
314 sprintf(b64, "LWS_%u_%u_COOKIE",
315 (unsigned int)tv.tv_sec,
316 (unsigned int)tv.tv_usec);
319 "Set-Cookie: test=LWS_%u_%u_COOKIE;Max-Age=360000\x0d\x0a",
320 (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
321 other_headers = leaf_path;
322 lwsl_err(other_headers);
325 if (libwebsockets_serve_http_file(context, wsi, buf,
326 mimetype, other_headers))
327 return -1; /* through completion or error, close the socket */
330 * notice that the sending of the file completes asynchronously,
331 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
337 case LWS_CALLBACK_HTTP_BODY:
338 strncpy(buf, in, 20);
343 lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
344 (const char *)buf, (int)len);
348 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
349 lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
350 /* the whole of the sent body arried, close the connection */
351 libwebsockets_return_http_status(context, wsi,
352 HTTP_STATUS_OK, NULL);
356 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
357 // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
358 /* kill the connection after we sent one file */
361 case LWS_CALLBACK_HTTP_WRITEABLE:
363 * we can send more of whatever it is we were sending
367 n = read(pss->fd, buffer, sizeof buffer);
368 /* problem reading, close conn */
371 /* sent it all, close conn */
375 * because it's HTTP and not websocket, don't need to take
376 * care about pre and postamble
378 m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
380 /* write failed, close conn */
383 /* partial write, adjust */
384 lseek(pss->fd, m - n, SEEK_CUR);
386 if (m) /* while still active, extend timeout */
387 libwebsocket_set_timeout(wsi,
388 PENDING_TIMEOUT_HTTP_CONTENT, 5);
390 /* if he has indigestion, let him clear it before eating more */
391 if (lws_partial_buffered(wsi))
394 } while (!lws_send_pipe_choked(wsi));
395 libwebsocket_callback_on_writable(context, wsi);
398 /* true if still partial pending */
399 if (lws_partial_buffered(wsi)) {
400 libwebsocket_callback_on_writable(context, wsi);
409 * callback for confirming to continue with client IP appear in
410 * protocol 0 callback since no websocket protocol has been agreed
411 * yet. You can just ignore this if you won't filter on client IP
412 * since the default uhandled callback return is 0 meaning let the
413 * connection continue.
416 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
418 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
419 sizeof(client_name), client_ip, sizeof(client_ip));
421 fprintf(stderr, "Received network connect from %s (%s)\n",
422 client_name, client_ip);
424 /* if we returned non-zero from here, we kill the connection */
429 * callbacks for managing the external poll() array appear in
430 * protocol 0 callback
433 case LWS_CALLBACK_LOCK_POLL:
435 * lock mutex to protect pollfd state
436 * called before any other POLL related callback
440 case LWS_CALLBACK_UNLOCK_POLL:
442 * unlock mutex to protect pollfd state when
443 * called after any other POLL related callback
447 case LWS_CALLBACK_ADD_POLL_FD:
449 if (count_pollfds >= max_poll_elements) {
450 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
454 fd_lookup[pa->fd] = count_pollfds;
455 pollfds[count_pollfds].fd = pa->fd;
456 pollfds[count_pollfds].events = pa->events;
457 pollfds[count_pollfds++].revents = 0;
460 case LWS_CALLBACK_DEL_POLL_FD:
461 if (!--count_pollfds)
463 m = fd_lookup[pa->fd];
464 /* have the last guy take up the vacant slot */
465 pollfds[m] = pollfds[count_pollfds];
466 fd_lookup[pollfds[count_pollfds].fd] = m;
469 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
470 pollfds[fd_lookup[pa->fd]].events = pa->events;
475 case LWS_CALLBACK_GET_THREAD_ID:
477 * if you will call "libwebsocket_callback_on_writable"
478 * from a different thread, return the caller thread ID
479 * here so lws can use this information to work out if it
480 * should signal the poll() loop to exit and restart early
483 /* return pthread_getthreadid_np(); */
495 /* dumb_increment protocol */
498 * one of these is auto-created for each connection and a pointer to the
499 * appropriate instance is passed to the callback in the user parameter
501 * for this example protocol we use it to individualize the count for each
505 struct per_session_data__dumb_increment {
510 callback_dumb_increment(struct libwebsocket_context *context,
511 struct libwebsocket *wsi,
512 enum libwebsocket_callback_reasons reason,
513 void *user, void *in, size_t len)
516 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
517 LWS_SEND_BUFFER_POST_PADDING];
518 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
519 struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
523 case LWS_CALLBACK_ESTABLISHED:
524 lwsl_info("callback_dumb_increment: "
525 "LWS_CALLBACK_ESTABLISHED\n");
529 case LWS_CALLBACK_SERVER_WRITEABLE:
530 n = sprintf((char *)p, "%d", pss->number++);
531 m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
533 lwsl_err("ERROR %d writing to di socket\n", n);
536 if (close_testing && pss->number == 50) {
537 lwsl_info("close tesing limit, closing\n");
542 case LWS_CALLBACK_RECEIVE:
543 // fprintf(stderr, "rx %d\n", (int)len);
546 if (strcmp((const char *)in, "reset\n") == 0)
550 * this just demonstrates how to use the protocol filter. If you won't
551 * study and reject connections based on header content, you don't need
552 * to handle this callback
555 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
556 dump_handshake_info(wsi);
557 /* you could return non-zero here and kill the connection */
568 /* lws-mirror_protocol */
570 #define MAX_MESSAGE_QUEUE 32
572 struct per_session_data__lws_mirror {
573 struct libwebsocket *wsi;
582 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
583 static int ringbuffer_head;
586 callback_lws_mirror(struct libwebsocket_context *context,
587 struct libwebsocket *wsi,
588 enum libwebsocket_callback_reasons reason,
589 void *user, void *in, size_t len)
592 struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
596 case LWS_CALLBACK_ESTABLISHED:
597 lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
598 pss->ringbuffer_tail = ringbuffer_head;
602 case LWS_CALLBACK_PROTOCOL_DESTROY:
603 lwsl_notice("mirror protocol cleaning up\n");
604 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
605 if (ringbuffer[n].payload)
606 free(ringbuffer[n].payload);
609 case LWS_CALLBACK_SERVER_WRITEABLE:
612 while (pss->ringbuffer_tail != ringbuffer_head) {
614 n = libwebsocket_write(wsi, (unsigned char *)
615 ringbuffer[pss->ringbuffer_tail].payload +
616 LWS_SEND_BUFFER_PRE_PADDING,
617 ringbuffer[pss->ringbuffer_tail].len,
620 lwsl_err("ERROR %d writing to mirror socket\n", n);
623 if (n < ringbuffer[pss->ringbuffer_tail].len)
624 lwsl_err("mirror partial write %d vs %d\n",
625 n, ringbuffer[pss->ringbuffer_tail].len);
627 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
628 pss->ringbuffer_tail = 0;
630 pss->ringbuffer_tail++;
632 if (((ringbuffer_head - pss->ringbuffer_tail) &
633 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
634 libwebsocket_rx_flow_allow_all_protocol(
635 libwebsockets_get_protocol(wsi));
637 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
639 if (lws_partial_buffered(wsi) || lws_send_pipe_choked(wsi)) {
640 libwebsocket_callback_on_writable(context, wsi);
644 * for tests with chrome on same machine as client and
645 * server, this is needed to stop chrome choking
655 case LWS_CALLBACK_RECEIVE:
657 if (((ringbuffer_head - pss->ringbuffer_tail) &
658 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
659 lwsl_err("dropping!\n");
663 if (ringbuffer[ringbuffer_head].payload)
664 free(ringbuffer[ringbuffer_head].payload);
666 ringbuffer[ringbuffer_head].payload =
667 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
668 LWS_SEND_BUFFER_POST_PADDING);
669 ringbuffer[ringbuffer_head].len = len;
670 memcpy((char *)ringbuffer[ringbuffer_head].payload +
671 LWS_SEND_BUFFER_PRE_PADDING, in, len);
672 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
677 if (((ringbuffer_head - pss->ringbuffer_tail) &
678 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
682 lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
683 libwebsocket_rx_flow_control(wsi, 0);
685 // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
687 libwebsocket_callback_on_writable_all_protocol(
688 libwebsockets_get_protocol(wsi));
692 * this just demonstrates how to use the protocol filter. If you won't
693 * study and reject connections based on header content, you don't need
694 * to handle this callback
697 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
698 dump_handshake_info(wsi);
699 /* you could return non-zero here and kill the connection */
710 /* list of supported protocols and callbacks */
712 static struct libwebsocket_protocols protocols[] = {
713 /* first protocol must always be HTTP handler */
716 "http-only", /* name */
717 callback_http, /* callback */
718 sizeof (struct per_session_data__http), /* per_session_data_size */
719 0, /* max frame size / rx buffer */
722 "dumb-increment-protocol",
723 callback_dumb_increment,
724 sizeof(struct per_session_data__dumb_increment),
728 "lws-mirror-protocol",
730 sizeof(struct per_session_data__lws_mirror),
733 { NULL, NULL, 0, 0 } /* terminator */
736 void sighandler(int sig)
739 libwebsocket_cancel_service(context);
742 static struct option options[] = {
743 { "help", no_argument, NULL, 'h' },
744 { "debug", required_argument, NULL, 'd' },
745 { "port", required_argument, NULL, 'p' },
746 { "ssl", no_argument, NULL, 's' },
747 { "allow-non-ssl", no_argument, NULL, 'a' },
748 { "interface", required_argument, NULL, 'i' },
749 { "closetest", no_argument, NULL, 'c' },
750 { "libev", no_argument, NULL, 'e' },
751 #ifndef LWS_NO_DAEMONIZE
752 { "daemonize", no_argument, NULL, 'D' },
754 { "resource_path", required_argument, NULL, 'r' },
758 int main(int argc, char **argv)
760 char cert_path[1024];
765 char interface_name[128] = "";
766 const char *iface = NULL;
768 int syslog_options = LOG_PID | LOG_PERROR;
770 unsigned int ms, oldms = 0;
771 struct lws_context_creation_info info;
774 #ifndef LWS_NO_DAEMONIZE
778 memset(&info, 0, sizeof info);
782 n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
787 opts |= LWS_SERVER_OPTION_LIBEV;
789 #ifndef LWS_NO_DAEMONIZE
793 syslog_options &= ~LOG_PERROR;
798 debug_level = atoi(optarg);
804 opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
807 info.port = atoi(optarg);
810 strncpy(interface_name, optarg, sizeof interface_name);
811 interface_name[(sizeof interface_name) - 1] = '\0';
812 iface = interface_name;
816 fprintf(stderr, " Close testing mode -- closes on "
817 "client after 50 dumb increments"
818 "and suppresses lws_mirror spam\n");
821 resource_path = optarg;
822 printf("Setting resource path to \"%s\"\n", resource_path);
825 fprintf(stderr, "Usage: test-server "
826 "[--port=<p>] [--ssl] "
827 "[-d <log bitfield>] "
828 "[--resource_path <path>]\n");
833 #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
835 * normally lock path would be /var/lock/lwsts or similar, to
836 * simplify getting started without having to take care about
837 * permissions or running as root, set to /tmp/.lwsts-lock
839 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
840 fprintf(stderr, "Failed to daemonize\n");
845 signal(SIGINT, sighandler);
848 /* we will only try to log things according to our debug_level */
849 setlogmask(LOG_UPTO (LOG_DEBUG));
850 openlog("lwsts", syslog_options, LOG_DAEMON);
853 /* tell the library what debug level to emit and to send it to syslog */
854 lws_set_log_level(debug_level, lwsl_emit_syslog);
856 lwsl_notice("libwebsockets test server - "
857 "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
858 "licensed under LGPL2.1\n");
860 max_poll_elements = getdtablesize();
861 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
862 fd_lookup = malloc(max_poll_elements * sizeof (int));
863 if (pollfds == NULL || fd_lookup == NULL) {
864 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
870 info.protocols = protocols;
871 #ifndef LWS_NO_EXTENSIONS
872 info.extensions = libwebsocket_get_internal_extensions();
875 info.ssl_cert_filepath = NULL;
876 info.ssl_private_key_filepath = NULL;
878 if (strlen(resource_path) > sizeof(cert_path) - 32) {
879 lwsl_err("resource path too long\n");
882 sprintf(cert_path, "%s/libwebsockets-test-server.pem",
884 if (strlen(resource_path) > sizeof(key_path) - 32) {
885 lwsl_err("resource path too long\n");
888 sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
891 info.ssl_cert_filepath = cert_path;
892 info.ssl_private_key_filepath = key_path;
898 context = libwebsocket_create_context(&info);
899 if (context == NULL) {
900 lwsl_err("libwebsocket init failed\n");
905 while (n >= 0 && !force_exit) {
908 gettimeofday(&tv, NULL);
911 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
912 * live websocket connection using the DUMB_INCREMENT protocol,
913 * as soon as it can take more packets (usually immediately)
916 ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
917 if ((ms - oldms) > 50) {
918 libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
925 * this represents an existing server's single poll action
926 * which also includes libwebsocket sockets
929 n = poll(pollfds, count_pollfds, 50);
935 for (n = 0; n < count_pollfds; n++)
936 if (pollfds[n].revents)
938 * returns immediately if the fd does not
939 * match anything under libwebsockets
942 if (libwebsocket_service_fd(context,
947 * If libwebsockets sockets are all we care about,
948 * you can use this api which takes care of the poll()
949 * and looping through finding who needed service.
951 * If no socket needs service, it'll return anyway after
952 * the number of ms in the second argument.
955 n = libwebsocket_service(context, 50);
963 libwebsocket_context_destroy(context);
965 lwsl_notice("libwebsockets-test-server exited cleanly\n");