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;
51 struct pollfd *pollfds;
55 static volatile int force_exit = 0;
56 static struct libwebsocket_context *context;
59 * This demo server shows how to use libwebsockets for one or more
60 * websocket protocols in the same server
62 * It defines the following websocket protocols:
64 * dumb-increment-protocol: once the socket is opened, an incrementing
65 * ascii string is sent down it every 50ms.
66 * If you send "reset\n" on the websocket, then
67 * the incrementing number is reset to 0.
69 * lws-mirror-protocol: copies any received packet to every connection also
70 * using this protocol, including the sender
77 PROTOCOL_DUMB_INCREMENT,
85 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
86 char *resource_path = LOCAL_RESOURCE_PATH;
89 * We take a strict whitelist approach to stop ../ attacks
97 struct per_session_data__http {
102 * this is just an example of parsing handshake headers, you don't need this
103 * in your code unless you will filter allowing connections by the header
108 dump_handshake_info(struct libwebsocket *wsi)
112 const unsigned char *c;
115 c = lws_token_to_string(n);
121 if (!lws_hdr_total_length(wsi, n)) {
126 lws_hdr_copy(wsi, buf, sizeof buf, n);
128 fprintf(stderr, " %s = %s\n", (char *)c, buf);
133 const char * get_mimetype(const char *file)
135 int n = strlen(file);
140 if (!strcmp(&file[n - 4], ".ico"))
141 return "image/x-icon";
143 if (!strcmp(&file[n - 4], ".png"))
146 if (!strcmp(&file[n - 5], ".html"))
152 /* this protocol server (always the first one) just knows how to do HTTP */
154 static int callback_http(struct libwebsocket_context *context,
155 struct libwebsocket *wsi,
156 enum libwebsocket_callback_reasons reason, void *user,
157 void *in, size_t len)
160 char leaf_path[1024];
166 static unsigned char buffer[4096];
167 struct stat stat_buf;
168 struct per_session_data__http *pss =
169 (struct per_session_data__http *)user;
170 const char *mimetype;
172 struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
176 case LWS_CALLBACK_HTTP:
178 dump_handshake_info(wsi);
181 libwebsockets_return_http_status(context, wsi,
182 HTTP_STATUS_BAD_REQUEST, NULL);
186 /* this example server has no concept of directories */
187 if (strchr((const char *)in + 1, '/')) {
188 libwebsockets_return_http_status(context, wsi,
189 HTTP_STATUS_FORBIDDEN, NULL);
193 /* if a legal POST URL, let it continue and accept data */
194 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
197 /* check for the "send a big file by hand" example case */
199 if (!strcmp((const char *)in, "/leaf.jpg")) {
200 if (strlen(resource_path) > sizeof(leaf_path) - 10)
202 sprintf(leaf_path, "%s/leaf.jpg", resource_path);
204 /* well, let's demonstrate how to send the hard way */
206 p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
207 end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
209 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
211 pss->fd = open(leaf_path, O_RDONLY);
217 if (fstat(pss->fd, &stat_buf) < 0)
221 * we will send a big jpeg file, but it could be
222 * anything. Set the Content-Type: appropriately
223 * so the browser knows what to do with it.
225 * Notice we use the APIs to build the header, which
226 * will do the right thing for HTTP 1/1.1 and HTTP2
227 * depending on what connection it happens to be working
230 if (lws_add_http_header_status(context, wsi, 200, &p, end))
232 if (lws_add_http_header_by_token(context, wsi,
233 WSI_TOKEN_HTTP_SERVER,
234 (unsigned char *)"libwebsockets",
237 if (lws_add_http_header_by_token(context, wsi,
238 WSI_TOKEN_HTTP_CONTENT_TYPE,
239 (unsigned char *)"image/jpeg",
242 if (lws_add_http_header_content_length(context, wsi,
243 stat_buf.st_size, &p, end))
245 if (lws_finalize_http_header(context, wsi, &p, end))
249 * send the http headers...
250 * this won't block since it's the first payload sent
251 * on the connection since it was established
252 * (too small for partial)
254 * Notice they are sent using LWS_WRITE_HTTP_HEADERS
255 * which also means you can't send body too in one step,
256 * this is mandated by changes in HTTP2
259 n = libwebsocket_write(wsi,
260 buffer + LWS_SEND_BUFFER_PRE_PADDING,
261 p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
262 LWS_WRITE_HTTP_HEADERS);
269 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
271 libwebsocket_callback_on_writable(context, wsi);
275 /* if not, send a file the easy way */
276 strcpy(buf, resource_path);
277 if (strcmp(in, "/")) {
278 if (*((const char *)in) != '/')
280 strncat(buf, in, sizeof(buf) - strlen(resource_path));
281 } else /* default file to serve */
282 strcat(buf, "/test.html");
283 buf[sizeof(buf) - 1] = '\0';
285 /* refuse to serve files we don't understand */
286 mimetype = get_mimetype(buf);
288 lwsl_err("Unknown mimetype for %s\n", buf);
289 libwebsockets_return_http_status(context, wsi,
290 HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
294 /* demostrates how to set a cookie on / */
296 other_headers = NULL;
298 if (!strcmp((const char *)in, "/") &&
299 !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
300 /* this isn't very unguessable but it'll do for us */
301 gettimeofday(&tv, NULL);
302 n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
303 (unsigned int)tv.tv_sec,
304 (unsigned int)tv.tv_usec);
306 p = (unsigned char *)leaf_path;
308 if (lws_add_http_header_by_name(context, wsi,
309 (unsigned char *)"set-cookie:",
310 (unsigned char *)b64, n, &p,
311 (unsigned char *)leaf_path + sizeof(leaf_path)))
313 n = (char *)p - leaf_path;
314 other_headers = leaf_path;
317 n = libwebsockets_serve_http_file(context, wsi, buf,
318 mimetype, other_headers, n);
319 if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
320 return -1; /* error or can't reuse connection: close the socket */
323 * notice that the sending of the file completes asynchronously,
324 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
330 case LWS_CALLBACK_HTTP_BODY:
331 strncpy(buf, in, 20);
336 lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
337 (const char *)buf, (int)len);
341 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
342 lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
343 /* the whole of the sent body arrived, close or reuse the connection */
344 libwebsockets_return_http_status(context, wsi,
345 HTTP_STATUS_OK, NULL);
348 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
349 // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
350 /* kill the connection after we sent one file */
353 case LWS_CALLBACK_HTTP_WRITEABLE:
355 * we can send more of whatever it is we were sending
358 /* we'd like the send this much */
359 n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
361 /* but if the peer told us he wants less, we can adapt */
362 m = lws_get_peer_write_allowance(wsi);
364 /* -1 means not using a protocol that has this info */
366 /* right now, peer can't handle anything */
369 if (m != -1 && m < n)
370 /* he couldn't handle that much */
373 n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
375 /* problem reading, close conn */
378 /* sent it all, close conn */
382 * To support HTTP2, must take care about preamble space
384 * identification of when we send the last payload frame
385 * is handled by the library itself if you sent a
386 * content-length header
388 m = libwebsocket_write(wsi,
389 buffer + LWS_SEND_BUFFER_PRE_PADDING,
392 /* write failed, close conn */
396 * http2 won't do this
399 /* partial write, adjust */
400 if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
403 if (m) /* while still active, extend timeout */
404 libwebsocket_set_timeout(wsi,
405 PENDING_TIMEOUT_HTTP_CONTENT, 5);
407 /* if we have indigestion, let him clear it before eating more */
408 if (lws_partial_buffered(wsi))
411 } while (!lws_send_pipe_choked(wsi));
414 libwebsocket_callback_on_writable(context, wsi);
417 /* true if still partial pending */
418 if (lws_partial_buffered(wsi)) {
419 libwebsocket_callback_on_writable(context, wsi);
430 * callback for confirming to continue with client IP appear in
431 * protocol 0 callback since no websocket protocol has been agreed
432 * yet. You can just ignore this if you won't filter on client IP
433 * since the default uhandled callback return is 0 meaning let the
434 * connection continue.
437 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
439 /* if we returned non-zero from here, we kill the connection */
444 * callbacks for managing the external poll() array appear in
445 * protocol 0 callback
448 case LWS_CALLBACK_LOCK_POLL:
450 * lock mutex to protect pollfd state
451 * called before any other POLL related callback
455 case LWS_CALLBACK_UNLOCK_POLL:
457 * unlock mutex to protect pollfd state when
458 * called after any other POLL related callback
462 case LWS_CALLBACK_ADD_POLL_FD:
464 if (count_pollfds >= max_poll_elements) {
465 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
469 fd_lookup[pa->fd] = count_pollfds;
470 pollfds[count_pollfds].fd = pa->fd;
471 pollfds[count_pollfds].events = pa->events;
472 pollfds[count_pollfds++].revents = 0;
475 case LWS_CALLBACK_DEL_POLL_FD:
476 if (!--count_pollfds)
478 m = fd_lookup[pa->fd];
479 /* have the last guy take up the vacant slot */
480 pollfds[m] = pollfds[count_pollfds];
481 fd_lookup[pollfds[count_pollfds].fd] = m;
484 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
485 pollfds[fd_lookup[pa->fd]].events = pa->events;
490 case LWS_CALLBACK_GET_THREAD_ID:
492 * if you will call "libwebsocket_callback_on_writable"
493 * from a different thread, return the caller thread ID
494 * here so lws can use this information to work out if it
495 * should signal the poll() loop to exit and restart early
498 /* return pthread_getthreadid_np(); */
509 if (lws_http_transaction_completed(wsi))
516 /* dumb_increment protocol */
519 * one of these is auto-created for each connection and a pointer to the
520 * appropriate instance is passed to the callback in the user parameter
522 * for this example protocol we use it to individualize the count for each
526 struct per_session_data__dumb_increment {
531 callback_dumb_increment(struct libwebsocket_context *context,
532 struct libwebsocket *wsi,
533 enum libwebsocket_callback_reasons reason,
534 void *user, void *in, size_t len)
537 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
538 LWS_SEND_BUFFER_POST_PADDING];
539 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
540 struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
544 case LWS_CALLBACK_ESTABLISHED:
545 lwsl_info("callback_dumb_increment: "
546 "LWS_CALLBACK_ESTABLISHED\n");
550 case LWS_CALLBACK_SERVER_WRITEABLE:
551 n = sprintf((char *)p, "%d", pss->number++);
552 m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
554 lwsl_err("ERROR %d writing to di socket\n", n);
557 if (close_testing && pss->number == 50) {
558 lwsl_info("close tesing limit, closing\n");
563 case LWS_CALLBACK_RECEIVE:
564 // fprintf(stderr, "rx %d\n", (int)len);
567 if (strcmp((const char *)in, "reset\n") == 0)
571 * this just demonstrates how to use the protocol filter. If you won't
572 * study and reject connections based on header content, you don't need
573 * to handle this callback
576 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
577 dump_handshake_info(wsi);
578 /* you could return non-zero here and kill the connection */
589 /* lws-mirror_protocol */
591 #define MAX_MESSAGE_QUEUE 32
593 struct per_session_data__lws_mirror {
594 struct libwebsocket *wsi;
603 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
604 static int ringbuffer_head;
607 callback_lws_mirror(struct libwebsocket_context *context,
608 struct libwebsocket *wsi,
609 enum libwebsocket_callback_reasons reason,
610 void *user, void *in, size_t len)
613 struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
617 case LWS_CALLBACK_ESTABLISHED:
618 lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
619 pss->ringbuffer_tail = ringbuffer_head;
623 case LWS_CALLBACK_PROTOCOL_DESTROY:
624 lwsl_notice("mirror protocol cleaning up\n");
625 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
626 if (ringbuffer[n].payload)
627 free(ringbuffer[n].payload);
630 case LWS_CALLBACK_SERVER_WRITEABLE:
633 while (pss->ringbuffer_tail != ringbuffer_head) {
635 n = libwebsocket_write(wsi, (unsigned char *)
636 ringbuffer[pss->ringbuffer_tail].payload +
637 LWS_SEND_BUFFER_PRE_PADDING,
638 ringbuffer[pss->ringbuffer_tail].len,
641 lwsl_err("ERROR %d writing to mirror socket\n", n);
644 if (n < ringbuffer[pss->ringbuffer_tail].len)
645 lwsl_err("mirror partial write %d vs %d\n",
646 n, ringbuffer[pss->ringbuffer_tail].len);
648 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
649 pss->ringbuffer_tail = 0;
651 pss->ringbuffer_tail++;
653 if (((ringbuffer_head - pss->ringbuffer_tail) &
654 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
655 libwebsocket_rx_flow_allow_all_protocol(
656 libwebsockets_get_protocol(wsi));
658 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
660 if (lws_partial_buffered(wsi) || lws_send_pipe_choked(wsi)) {
661 libwebsocket_callback_on_writable(context, wsi);
665 * for tests with chrome on same machine as client and
666 * server, this is needed to stop chrome choking
676 case LWS_CALLBACK_RECEIVE:
678 if (((ringbuffer_head - pss->ringbuffer_tail) &
679 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
680 lwsl_err("dropping!\n");
684 if (ringbuffer[ringbuffer_head].payload)
685 free(ringbuffer[ringbuffer_head].payload);
687 ringbuffer[ringbuffer_head].payload =
688 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
689 LWS_SEND_BUFFER_POST_PADDING);
690 ringbuffer[ringbuffer_head].len = len;
691 memcpy((char *)ringbuffer[ringbuffer_head].payload +
692 LWS_SEND_BUFFER_PRE_PADDING, in, len);
693 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
698 if (((ringbuffer_head - pss->ringbuffer_tail) &
699 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
703 lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
704 libwebsocket_rx_flow_control(wsi, 0);
706 // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
708 libwebsocket_callback_on_writable_all_protocol(
709 libwebsockets_get_protocol(wsi));
713 * this just demonstrates how to use the protocol filter. If you won't
714 * study and reject connections based on header content, you don't need
715 * to handle this callback
718 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
719 dump_handshake_info(wsi);
720 /* you could return non-zero here and kill the connection */
731 /* list of supported protocols and callbacks */
733 static struct libwebsocket_protocols protocols[] = {
734 /* first protocol must always be HTTP handler */
737 "http-only", /* name */
738 callback_http, /* callback */
739 sizeof (struct per_session_data__http), /* per_session_data_size */
740 0, /* max frame size / rx buffer */
743 "dumb-increment-protocol",
744 callback_dumb_increment,
745 sizeof(struct per_session_data__dumb_increment),
749 "lws-mirror-protocol",
751 sizeof(struct per_session_data__lws_mirror),
754 { NULL, NULL, 0, 0 } /* terminator */
757 void sighandler(int sig)
760 libwebsocket_cancel_service(context);
763 static struct option options[] = {
764 { "help", no_argument, NULL, 'h' },
765 { "debug", required_argument, NULL, 'd' },
766 { "port", required_argument, NULL, 'p' },
767 { "ssl", no_argument, NULL, 's' },
768 { "allow-non-ssl", no_argument, NULL, 'a' },
769 { "interface", required_argument, NULL, 'i' },
770 { "closetest", no_argument, NULL, 'c' },
771 { "libev", no_argument, NULL, 'e' },
772 #ifndef LWS_NO_DAEMONIZE
773 { "daemonize", no_argument, NULL, 'D' },
775 { "resource_path", required_argument, NULL, 'r' },
779 int main(int argc, char **argv)
781 char cert_path[1024];
786 char interface_name[128] = "";
787 const char *iface = NULL;
789 int syslog_options = LOG_PID | LOG_PERROR;
791 unsigned int ms, oldms = 0;
792 struct lws_context_creation_info info;
795 #ifndef LWS_NO_DAEMONIZE
799 memset(&info, 0, sizeof info);
803 n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
808 opts |= LWS_SERVER_OPTION_LIBEV;
810 #ifndef LWS_NO_DAEMONIZE
814 syslog_options &= ~LOG_PERROR;
819 debug_level = atoi(optarg);
825 opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
828 info.port = atoi(optarg);
831 strncpy(interface_name, optarg, sizeof interface_name);
832 interface_name[(sizeof interface_name) - 1] = '\0';
833 iface = interface_name;
837 fprintf(stderr, " Close testing mode -- closes on "
838 "client after 50 dumb increments"
839 "and suppresses lws_mirror spam\n");
842 resource_path = optarg;
843 printf("Setting resource path to \"%s\"\n", resource_path);
846 fprintf(stderr, "Usage: test-server "
847 "[--port=<p>] [--ssl] "
848 "[-d <log bitfield>] "
849 "[--resource_path <path>]\n");
854 #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
856 * normally lock path would be /var/lock/lwsts or similar, to
857 * simplify getting started without having to take care about
858 * permissions or running as root, set to /tmp/.lwsts-lock
860 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
861 fprintf(stderr, "Failed to daemonize\n");
866 signal(SIGINT, sighandler);
869 /* we will only try to log things according to our debug_level */
870 setlogmask(LOG_UPTO (LOG_DEBUG));
871 openlog("lwsts", syslog_options, LOG_DAEMON);
874 /* tell the library what debug level to emit and to send it to syslog */
875 lws_set_log_level(debug_level, lwsl_emit_syslog);
877 lwsl_notice("libwebsockets test server - "
878 "(C) Copyright 2010-2015 Andy Green <andy@warmcat.com> - "
879 "licensed under LGPL2.1\n");
881 printf("Using resource path \"%s\"\n", resource_path);
883 max_poll_elements = getdtablesize();
884 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
885 fd_lookup = malloc(max_poll_elements * sizeof (int));
886 if (pollfds == NULL || fd_lookup == NULL) {
887 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
893 info.protocols = protocols;
894 #ifndef LWS_NO_EXTENSIONS
895 info.extensions = libwebsocket_get_internal_extensions();
898 info.ssl_cert_filepath = NULL;
899 info.ssl_private_key_filepath = NULL;
901 if (strlen(resource_path) > sizeof(cert_path) - 32) {
902 lwsl_err("resource path too long\n");
905 sprintf(cert_path, "%s/libwebsockets-test-server.pem",
907 if (strlen(resource_path) > sizeof(key_path) - 32) {
908 lwsl_err("resource path too long\n");
911 sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
914 info.ssl_cert_filepath = cert_path;
915 info.ssl_private_key_filepath = key_path;
921 context = libwebsocket_create_context(&info);
922 if (context == NULL) {
923 lwsl_err("libwebsocket init failed\n");
928 while (n >= 0 && !force_exit) {
931 gettimeofday(&tv, NULL);
934 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
935 * live websocket connection using the DUMB_INCREMENT protocol,
936 * as soon as it can take more packets (usually immediately)
939 ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
940 if ((ms - oldms) > 50) {
941 libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
948 * this represents an existing server's single poll action
949 * which also includes libwebsocket sockets
952 n = poll(pollfds, count_pollfds, 50);
958 for (n = 0; n < count_pollfds; n++)
959 if (pollfds[n].revents)
961 * returns immediately if the fd does not
962 * match anything under libwebsockets
965 if (libwebsocket_service_fd(context,
970 * If libwebsockets sockets are all we care about,
971 * you can use this api which takes care of the poll()
972 * and looping through finding who needed service.
974 * If no socket needs service, it'll return anyway after
975 * the number of ms in the second argument.
978 n = libwebsocket_service(context, 50);
986 libwebsocket_context_destroy(context);
988 lwsl_notice("libwebsockets-test-server exited cleanly\n");