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)
110 const unsigned char *c;
113 c = lws_token_to_string(n);
119 if (!lws_hdr_total_length(wsi, n)) {
124 lws_hdr_copy(wsi, buf, sizeof buf, n);
126 fprintf(stderr, " %s = %s\n", (char *)c, buf);
131 const char * get_mimetype(const char *file)
133 int n = strlen(file);
138 if (!strcmp(&file[n - 4], ".ico"))
139 return "image/x-icon";
141 if (!strcmp(&file[n - 4], ".png"))
144 if (!strcmp(&file[n - 5], ".html"))
150 /* this protocol server (always the first one) just knows how to do HTTP */
152 static int callback_http(struct libwebsocket_context *context,
153 struct libwebsocket *wsi,
154 enum libwebsocket_callback_reasons reason, void *user,
155 void *in, size_t len)
158 char client_name[128];
162 char leaf_path[1024];
168 static unsigned char buffer[4096];
169 struct stat stat_buf;
170 struct per_session_data__http *pss =
171 (struct per_session_data__http *)user;
172 const char *mimetype;
174 struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
178 case LWS_CALLBACK_HTTP:
180 dump_handshake_info(wsi);
183 libwebsockets_return_http_status(context, wsi,
184 HTTP_STATUS_BAD_REQUEST, NULL);
188 /* this example server has no concept of directories */
189 if (strchr((const char *)in + 1, '/')) {
190 libwebsockets_return_http_status(context, wsi,
191 HTTP_STATUS_FORBIDDEN, NULL);
195 /* if a legal POST URL, let it continue and accept data */
196 if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
199 /* check for the "send a big file by hand" example case */
201 if (!strcmp((const char *)in, "/leaf.jpg")) {
202 if (strlen(resource_path) > sizeof(leaf_path) - 10)
204 sprintf(leaf_path, "%s/leaf.jpg", resource_path);
206 /* well, let's demonstrate how to send the hard way */
208 p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
209 end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
211 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
213 pss->fd = open(leaf_path, O_RDONLY);
219 fstat(pss->fd, &stat_buf);
222 * we will send a big jpeg file, but it could be
223 * anything. Set the Content-Type: appropriately
224 * so the browser knows what to do with it.
226 * Notice we use the APIs to build the header, which
227 * will do the right thing for HTTP 1/1.1 and HTTP2
228 * depending on what connection it happens to be working
231 if (lws_add_http_header_status(context, wsi, 200, &p, end))
233 if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end))
235 if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"image/jpeg", 10, &p, end))
237 if (lws_add_http_header_content_length(context, wsi,stat_buf.st_size, &p, end))
239 if (lws_finalize_http_header(context, wsi, &p, end))
243 * send the http headers...
244 * this won't block since it's the first payload sent
245 * on the connection since it was established
246 * (too small for partial)
248 * Notice they are sent using LWS_WRITE_HTTP_HEADERS
249 * which also means you can't send body too in one step,
250 * this is mandated by changes in HTTP2
253 n = libwebsocket_write(wsi,
254 buffer + LWS_SEND_BUFFER_PRE_PADDING,
255 p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
256 LWS_WRITE_HTTP_HEADERS);
263 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
265 libwebsocket_callback_on_writable(context, wsi);
269 /* if not, send a file the easy way */
270 strcpy(buf, resource_path);
271 if (strcmp(in, "/")) {
272 if (*((const char *)in) != '/')
274 strncat(buf, in, sizeof(buf) - strlen(resource_path));
275 } else /* default file to serve */
276 strcat(buf, "/test.html");
277 buf[sizeof(buf) - 1] = '\0';
279 /* refuse to serve files we don't understand */
280 mimetype = get_mimetype(buf);
282 lwsl_err("Unknown mimetype for %s\n", buf);
283 libwebsockets_return_http_status(context, wsi,
284 HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
288 /* demostrates how to set a cookie on / */
290 other_headers = NULL;
292 if (!strcmp((const char *)in, "/") &&
293 !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
294 /* this isn't very unguessable but it'll do for us */
295 gettimeofday(&tv, NULL);
296 n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
297 (unsigned int)tv.tv_sec,
298 (unsigned int)tv.tv_usec);
300 p = (unsigned char *)leaf_path;
302 if (lws_add_http_header_by_name(context, wsi,
303 (unsigned char *)"set-cookie:",
304 (unsigned char *)b64, n, &p,
305 (unsigned char *)leaf_path + sizeof(leaf_path)))
307 n = (char *)p - leaf_path;
308 other_headers = leaf_path;
311 n = libwebsockets_serve_http_file(context, wsi, buf,
312 mimetype, other_headers, n);
313 if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
314 return -1; /* error or can't reuse connection: close the socket */
317 * notice that the sending of the file completes asynchronously,
318 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
324 case LWS_CALLBACK_HTTP_BODY:
325 strncpy(buf, in, 20);
330 lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
331 (const char *)buf, (int)len);
335 case LWS_CALLBACK_HTTP_BODY_COMPLETION:
336 lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
337 /* the whole of the sent body arrived, close or reuse the connection */
338 libwebsockets_return_http_status(context, wsi,
339 HTTP_STATUS_OK, NULL);
342 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
343 // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
344 /* kill the connection after we sent one file */
347 case LWS_CALLBACK_HTTP_WRITEABLE:
349 * we can send more of whatever it is we were sending
352 /* we'd like the send this much */
353 n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
355 /* but if the peer told us he wants less, we can adapt */
356 m = lws_get_peer_write_allowance(wsi);
358 /* -1 means not using a protocol that has this info */
360 /* right now, peer can't handle anything */
363 if (m != -1 && m < n)
364 /* he couldn't handle that much */
367 n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
369 /* problem reading, close conn */
372 /* sent it all, close conn */
376 * To support HTTP2, must take care about preamble space
378 * identification of when we send the last payload frame
379 * is handled by the library itself if you sent a
380 * content-length header
382 m = libwebsocket_write(wsi,
383 buffer + LWS_SEND_BUFFER_PRE_PADDING,
386 /* write failed, close conn */
390 * http2 won't do this
393 /* partial write, adjust */
394 lseek(pss->fd, m - n, SEEK_CUR);
396 if (m) /* while still active, extend timeout */
397 libwebsocket_set_timeout(wsi,
398 PENDING_TIMEOUT_HTTP_CONTENT, 5);
400 /* if we have indigestion, let him clear it before eating more */
401 if (lws_partial_buffered(wsi))
404 } while (!lws_send_pipe_choked(wsi));
407 libwebsocket_callback_on_writable(context, wsi);
410 /* true if still partial pending */
411 if (lws_partial_buffered(wsi)) {
412 libwebsocket_callback_on_writable(context, wsi);
423 * callback for confirming to continue with client IP appear in
424 * protocol 0 callback since no websocket protocol has been agreed
425 * yet. You can just ignore this if you won't filter on client IP
426 * since the default uhandled callback return is 0 meaning let the
427 * connection continue.
430 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
432 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
433 sizeof(client_name), client_ip, sizeof(client_ip));
435 fprintf(stderr, "Received network connect from %s (%s)\n",
436 client_name, client_ip);
438 /* if we returned non-zero from here, we kill the connection */
443 * callbacks for managing the external poll() array appear in
444 * protocol 0 callback
447 case LWS_CALLBACK_LOCK_POLL:
449 * lock mutex to protect pollfd state
450 * called before any other POLL related callback
454 case LWS_CALLBACK_UNLOCK_POLL:
456 * unlock mutex to protect pollfd state when
457 * called after any other POLL related callback
461 case LWS_CALLBACK_ADD_POLL_FD:
463 if (count_pollfds >= max_poll_elements) {
464 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
468 fd_lookup[pa->fd] = count_pollfds;
469 pollfds[count_pollfds].fd = pa->fd;
470 pollfds[count_pollfds].events = pa->events;
471 pollfds[count_pollfds++].revents = 0;
474 case LWS_CALLBACK_DEL_POLL_FD:
475 if (!--count_pollfds)
477 m = fd_lookup[pa->fd];
478 /* have the last guy take up the vacant slot */
479 pollfds[m] = pollfds[count_pollfds];
480 fd_lookup[pollfds[count_pollfds].fd] = m;
483 case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
484 pollfds[fd_lookup[pa->fd]].events = pa->events;
489 case LWS_CALLBACK_GET_THREAD_ID:
491 * if you will call "libwebsocket_callback_on_writable"
492 * from a different thread, return the caller thread ID
493 * here so lws can use this information to work out if it
494 * should signal the poll() loop to exit and restart early
497 /* return pthread_getthreadid_np(); */
508 if (lws_http_transaction_completed(wsi))
515 /* dumb_increment protocol */
518 * one of these is auto-created for each connection and a pointer to the
519 * appropriate instance is passed to the callback in the user parameter
521 * for this example protocol we use it to individualize the count for each
525 struct per_session_data__dumb_increment {
530 callback_dumb_increment(struct libwebsocket_context *context,
531 struct libwebsocket *wsi,
532 enum libwebsocket_callback_reasons reason,
533 void *user, void *in, size_t len)
536 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
537 LWS_SEND_BUFFER_POST_PADDING];
538 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
539 struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
543 case LWS_CALLBACK_ESTABLISHED:
544 lwsl_info("callback_dumb_increment: "
545 "LWS_CALLBACK_ESTABLISHED\n");
549 case LWS_CALLBACK_SERVER_WRITEABLE:
550 n = sprintf((char *)p, "%d", pss->number++);
551 m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
553 lwsl_err("ERROR %d writing to di socket\n", n);
556 if (close_testing && pss->number == 50) {
557 lwsl_info("close tesing limit, closing\n");
562 case LWS_CALLBACK_RECEIVE:
563 // fprintf(stderr, "rx %d\n", (int)len);
566 if (strcmp((const char *)in, "reset\n") == 0)
570 * this just demonstrates how to use the protocol filter. If you won't
571 * study and reject connections based on header content, you don't need
572 * to handle this callback
575 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
576 dump_handshake_info(wsi);
577 /* you could return non-zero here and kill the connection */
588 /* lws-mirror_protocol */
590 #define MAX_MESSAGE_QUEUE 32
592 struct per_session_data__lws_mirror {
593 struct libwebsocket *wsi;
602 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
603 static int ringbuffer_head;
606 callback_lws_mirror(struct libwebsocket_context *context,
607 struct libwebsocket *wsi,
608 enum libwebsocket_callback_reasons reason,
609 void *user, void *in, size_t len)
612 struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
616 case LWS_CALLBACK_ESTABLISHED:
617 lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
618 pss->ringbuffer_tail = ringbuffer_head;
622 case LWS_CALLBACK_PROTOCOL_DESTROY:
623 lwsl_notice("mirror protocol cleaning up\n");
624 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
625 if (ringbuffer[n].payload)
626 free(ringbuffer[n].payload);
629 case LWS_CALLBACK_SERVER_WRITEABLE:
632 while (pss->ringbuffer_tail != ringbuffer_head) {
634 n = libwebsocket_write(wsi, (unsigned char *)
635 ringbuffer[pss->ringbuffer_tail].payload +
636 LWS_SEND_BUFFER_PRE_PADDING,
637 ringbuffer[pss->ringbuffer_tail].len,
640 lwsl_err("ERROR %d writing to mirror socket\n", n);
643 if (n < ringbuffer[pss->ringbuffer_tail].len)
644 lwsl_err("mirror partial write %d vs %d\n",
645 n, ringbuffer[pss->ringbuffer_tail].len);
647 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
648 pss->ringbuffer_tail = 0;
650 pss->ringbuffer_tail++;
652 if (((ringbuffer_head - pss->ringbuffer_tail) &
653 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
654 libwebsocket_rx_flow_allow_all_protocol(
655 libwebsockets_get_protocol(wsi));
657 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
659 if (lws_partial_buffered(wsi) || lws_send_pipe_choked(wsi)) {
660 libwebsocket_callback_on_writable(context, wsi);
664 * for tests with chrome on same machine as client and
665 * server, this is needed to stop chrome choking
675 case LWS_CALLBACK_RECEIVE:
677 if (((ringbuffer_head - pss->ringbuffer_tail) &
678 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
679 lwsl_err("dropping!\n");
683 if (ringbuffer[ringbuffer_head].payload)
684 free(ringbuffer[ringbuffer_head].payload);
686 ringbuffer[ringbuffer_head].payload =
687 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
688 LWS_SEND_BUFFER_POST_PADDING);
689 ringbuffer[ringbuffer_head].len = len;
690 memcpy((char *)ringbuffer[ringbuffer_head].payload +
691 LWS_SEND_BUFFER_PRE_PADDING, in, len);
692 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
697 if (((ringbuffer_head - pss->ringbuffer_tail) &
698 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
702 lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
703 libwebsocket_rx_flow_control(wsi, 0);
705 // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
707 libwebsocket_callback_on_writable_all_protocol(
708 libwebsockets_get_protocol(wsi));
712 * this just demonstrates how to use the protocol filter. If you won't
713 * study and reject connections based on header content, you don't need
714 * to handle this callback
717 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
718 dump_handshake_info(wsi);
719 /* you could return non-zero here and kill the connection */
730 /* list of supported protocols and callbacks */
732 static struct libwebsocket_protocols protocols[] = {
733 /* first protocol must always be HTTP handler */
736 "http-only", /* name */
737 callback_http, /* callback */
738 sizeof (struct per_session_data__http), /* per_session_data_size */
739 0, /* max frame size / rx buffer */
742 "dumb-increment-protocol",
743 callback_dumb_increment,
744 sizeof(struct per_session_data__dumb_increment),
748 "lws-mirror-protocol",
750 sizeof(struct per_session_data__lws_mirror),
753 { NULL, NULL, 0, 0 } /* terminator */
756 void sighandler(int sig)
759 libwebsocket_cancel_service(context);
762 static struct option options[] = {
763 { "help", no_argument, NULL, 'h' },
764 { "debug", required_argument, NULL, 'd' },
765 { "port", required_argument, NULL, 'p' },
766 { "ssl", no_argument, NULL, 's' },
767 { "allow-non-ssl", no_argument, NULL, 'a' },
768 { "interface", required_argument, NULL, 'i' },
769 { "closetest", no_argument, NULL, 'c' },
770 { "libev", no_argument, NULL, 'e' },
771 #ifndef LWS_NO_DAEMONIZE
772 { "daemonize", no_argument, NULL, 'D' },
774 { "resource_path", required_argument, NULL, 'r' },
778 int main(int argc, char **argv)
780 char cert_path[1024];
785 char interface_name[128] = "";
786 const char *iface = NULL;
788 int syslog_options = LOG_PID | LOG_PERROR;
790 unsigned int ms, oldms = 0;
791 struct lws_context_creation_info info;
794 #ifndef LWS_NO_DAEMONIZE
798 memset(&info, 0, sizeof info);
802 n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
807 opts |= LWS_SERVER_OPTION_LIBEV;
809 #ifndef LWS_NO_DAEMONIZE
813 syslog_options &= ~LOG_PERROR;
818 debug_level = atoi(optarg);
824 opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
827 info.port = atoi(optarg);
830 strncpy(interface_name, optarg, sizeof interface_name);
831 interface_name[(sizeof interface_name) - 1] = '\0';
832 iface = interface_name;
836 fprintf(stderr, " Close testing mode -- closes on "
837 "client after 50 dumb increments"
838 "and suppresses lws_mirror spam\n");
841 resource_path = optarg;
842 printf("Setting resource path to \"%s\"\n", resource_path);
845 fprintf(stderr, "Usage: test-server "
846 "[--port=<p>] [--ssl] "
847 "[-d <log bitfield>] "
848 "[--resource_path <path>]\n");
853 #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
855 * normally lock path would be /var/lock/lwsts or similar, to
856 * simplify getting started without having to take care about
857 * permissions or running as root, set to /tmp/.lwsts-lock
859 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
860 fprintf(stderr, "Failed to daemonize\n");
865 signal(SIGINT, sighandler);
868 /* we will only try to log things according to our debug_level */
869 setlogmask(LOG_UPTO (LOG_DEBUG));
870 openlog("lwsts", syslog_options, LOG_DAEMON);
873 /* tell the library what debug level to emit and to send it to syslog */
874 lws_set_log_level(debug_level, lwsl_emit_syslog);
876 lwsl_notice("libwebsockets test server - "
877 "(C) Copyright 2010-2014 Andy Green <andy@warmcat.com> - "
878 "licensed under LGPL2.1\n");
880 printf("Using resource path \"%s\"\n", resource_path);
882 max_poll_elements = getdtablesize();
883 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
884 fd_lookup = malloc(max_poll_elements * sizeof (int));
885 if (pollfds == NULL || fd_lookup == NULL) {
886 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
892 info.protocols = protocols;
893 #ifndef LWS_NO_EXTENSIONS
894 info.extensions = libwebsocket_get_internal_extensions();
897 info.ssl_cert_filepath = NULL;
898 info.ssl_private_key_filepath = NULL;
900 if (strlen(resource_path) > sizeof(cert_path) - 32) {
901 lwsl_err("resource path too long\n");
904 sprintf(cert_path, "%s/libwebsockets-test-server.pem",
906 if (strlen(resource_path) > sizeof(key_path) - 32) {
907 lwsl_err("resource path too long\n");
910 sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
913 info.ssl_cert_filepath = cert_path;
914 info.ssl_private_key_filepath = key_path;
920 context = libwebsocket_create_context(&info);
921 if (context == NULL) {
922 lwsl_err("libwebsocket init failed\n");
927 while (n >= 0 && !force_exit) {
930 gettimeofday(&tv, NULL);
933 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
934 * live websocket connection using the DUMB_INCREMENT protocol,
935 * as soon as it can take more packets (usually immediately)
938 ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
939 if ((ms - oldms) > 50) {
940 libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
947 * this represents an existing server's single poll action
948 * which also includes libwebsocket sockets
951 n = poll(pollfds, count_pollfds, 50);
957 for (n = 0; n < count_pollfds; n++)
958 if (pollfds[n].revents)
960 * returns immediately if the fd does not
961 * match anything under libwebsockets
964 if (libwebsocket_service_fd(context,
969 * If libwebsockets sockets are all we care about,
970 * you can use this api which takes care of the poll()
971 * and looping through finding who needed service.
973 * If no socket needs service, it'll return anyway after
974 * the number of ms in the second argument.
977 n = libwebsocket_service(context, 50);
985 libwebsocket_context_destroy(context);
987 lwsl_notice("libwebsockets-test-server exited cleanly\n");