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"
37 #ifndef WIN32_LEAN_AND_MEAN
38 #define WIN32_LEAN_AND_MEAN
44 #include "websock-w32.h"
53 #include "../lib/libwebsockets.h"
55 static int close_testing;
56 int max_poll_elements;
58 struct pollfd *pollfds;
64 * This demo server shows how to use libwebsockets for one or more
65 * websocket protocols in the same server
67 * It defines the following websocket protocols:
69 * dumb-increment-protocol: once the socket is opened, an incrementing
70 * ascii string is sent down it every 50ms.
71 * If you send "reset\n" on the websocket, then
72 * the incrementing number is reset to 0.
74 * lws-mirror-protocol: copies any received packet to every connection also
75 * using this protocol, including the sender
82 PROTOCOL_DUMB_INCREMENT,
90 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
91 char *resource_path = LOCAL_RESOURCE_PATH;
94 * We take a strict whitelist approach to stop ../ attacks
102 struct per_session_data__http {
106 const char * get_mimetype(const char *file)
108 int n = strlen(file);
113 if (!strcmp(&file[n - 4], ".ico"))
114 return "image/x-icon";
116 if (!strcmp(&file[n - 4], ".png"))
119 if (!strcmp(&file[n - 5], ".html"))
125 /* this protocol server (always the first one) just knows how to do HTTP */
127 static int callback_http(struct libwebsocket_context *context,
128 struct libwebsocket *wsi,
129 enum libwebsocket_callback_reasons reason, void *user,
130 void *in, size_t len)
133 char client_name[128];
137 char leaf_path[1024];
143 static unsigned char buffer[4096];
144 struct stat stat_buf;
145 struct per_session_data__http *pss =
146 (struct per_session_data__http *)user;
147 const char *mimetype;
149 int fd = (int)(long)in;
153 case LWS_CALLBACK_HTTP:
156 libwebsockets_return_http_status(context, wsi,
157 HTTP_STATUS_BAD_REQUEST, NULL);
161 /* this server has no concept of directories */
162 if (strchr((const char *)in + 1, '/')) {
163 libwebsockets_return_http_status(context, wsi,
164 HTTP_STATUS_FORBIDDEN, NULL);
168 /* check for the "send a big file by hand" example case */
170 if (!strcmp((const char *)in, "/leaf.jpg")) {
171 if (strlen(resource_path) > sizeof(leaf_path) - 10)
173 sprintf(leaf_path, "%s/leaf.jpg", resource_path);
175 /* well, let's demonstrate how to send the hard way */
180 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
182 pss->fd = open(leaf_path, O_RDONLY);
188 fstat(pss->fd, &stat_buf);
191 * we will send a big jpeg file, but it could be
192 * anything. Set the Content-Type: appropriately
193 * so the browser knows what to do with it.
196 p += sprintf((char *)p,
197 "HTTP/1.0 200 OK\x0d\x0a"
198 "Server: libwebsockets\x0d\x0a"
199 "Content-Type: image/jpeg\x0d\x0a"
200 "Content-Length: %u\x0d\x0a\x0d\x0a",
201 (unsigned int)stat_buf.st_size);
204 * send the http headers...
205 * this won't block since it's the first payload sent
206 * on the connection since it was established
207 * (too small for partial)
210 n = libwebsocket_write(wsi, buffer,
211 p - buffer, LWS_WRITE_HTTP);
218 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
220 libwebsocket_callback_on_writable(context, wsi);
224 /* if not, send a file the easy way */
225 strcpy(buf, resource_path);
226 if (strcmp(in, "/")) {
227 if (*((const char *)in) != '/')
229 strncat(buf, in, sizeof(buf) - strlen(resource_path));
230 } else /* default file to serve */
231 strcat(buf, "/test.html");
232 buf[sizeof(buf) - 1] = '\0';
234 /* refuse to serve files we don't understand */
235 mimetype = get_mimetype(buf);
237 lwsl_err("Unknown mimetype for %s\n", buf);
238 libwebsockets_return_http_status(context, wsi,
239 HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
243 /* demostrates how to set a cookie on / */
245 other_headers = NULL;
246 if (!strcmp((const char *)in, "/") &&
247 !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
248 /* this isn't very unguessable but it'll do for us */
249 gettimeofday(&tv, NULL);
250 sprintf(b64, "LWS_%u_%u_COOKIE",
251 (unsigned int)tv.tv_sec,
252 (unsigned int)tv.tv_usec);
255 "Set-Cookie: test=LWS_%u_%u_COOKIE;Max-Age=360000\x0d\x0a",
256 (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
257 other_headers = leaf_path;
258 lwsl_err(other_headers);
261 if (libwebsockets_serve_http_file(context, wsi, buf,
262 mimetype, other_headers))
263 return -1; /* through completion or error, close the socket */
266 * notice that the sending of the file completes asynchronously,
267 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
273 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
274 // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
275 /* kill the connection after we sent one file */
278 case LWS_CALLBACK_HTTP_WRITEABLE:
280 * we can send more of whatever it is we were sending
284 n = read(pss->fd, buffer, sizeof buffer);
285 /* problem reading, close conn */
288 /* sent it all, close conn */
292 * because it's HTTP and not websocket, don't need to take
293 * care about pre and postamble
295 m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
297 /* write failed, close conn */
300 /* partial write, adjust */
301 lseek(pss->fd, m - n, SEEK_CUR);
303 } while (!lws_send_pipe_choked(wsi));
304 libwebsocket_callback_on_writable(context, wsi);
312 * callback for confirming to continue with client IP appear in
313 * protocol 0 callback since no websocket protocol has been agreed
314 * yet. You can just ignore this if you won't filter on client IP
315 * since the default uhandled callback return is 0 meaning let the
316 * connection continue.
319 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
321 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
322 sizeof(client_name), client_ip, sizeof(client_ip));
324 fprintf(stderr, "Received network connect from %s (%s)\n",
325 client_name, client_ip);
327 /* if we returned non-zero from here, we kill the connection */
332 * callbacks for managing the external poll() array appear in
333 * protocol 0 callback
336 case LWS_CALLBACK_ADD_POLL_FD:
338 if (count_pollfds >= max_poll_elements) {
339 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
343 fd_lookup[fd] = count_pollfds;
344 pollfds[count_pollfds].fd = fd;
345 pollfds[count_pollfds].events = (int)(long)len;
346 pollfds[count_pollfds++].revents = 0;
349 case LWS_CALLBACK_DEL_POLL_FD:
350 if (!--count_pollfds)
353 /* have the last guy take up the vacant slot */
354 pollfds[m] = pollfds[count_pollfds];
355 fd_lookup[pollfds[count_pollfds].fd] = m;
358 case LWS_CALLBACK_SET_MODE_POLL_FD:
359 pollfds[fd_lookup[fd]].events |= (int)(long)len;
362 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
363 pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
375 * this is just an example of parsing handshake headers, you don't need this
376 * in your code unless you will filter allowing connections by the header
381 dump_handshake_info(struct libwebsocket *wsi)
384 static const char *token_names[] = {
385 /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
386 /*[WSI_TOKEN_HOST] =*/ "Host",
387 /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
388 /*[WSI_TOKEN_KEY1] =*/ "key 1",
389 /*[WSI_TOKEN_KEY2] =*/ "key 2",
390 /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
391 /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
392 /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
393 /*[WSI_TOKEN_DRAFT] =*/ "Draft",
394 /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
397 /*[WSI_TOKEN_KEY] =*/ "Key",
398 /*[WSI_TOKEN_VERSION] =*/ "Version",
399 /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
402 /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
404 /* client receives these */
405 /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
406 /*[WSI_TOKEN_NONCE] =*/ "Nonce",
407 /*[WSI_TOKEN_HTTP] =*/ "Http",
410 "If-Modified-Since:",
422 /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
426 for (n = 0; n < sizeof(token_names) / sizeof(token_names[0]); n++) {
427 if (!lws_hdr_total_length(wsi, n))
430 lws_hdr_copy(wsi, buf, sizeof buf, n);
432 fprintf(stderr, " %s = %s\n", token_names[n], buf);
436 /* dumb_increment protocol */
439 * one of these is auto-created for each connection and a pointer to the
440 * appropriate instance is passed to the callback in the user parameter
442 * for this example protocol we use it to individualize the count for each
446 struct per_session_data__dumb_increment {
451 callback_dumb_increment(struct libwebsocket_context *context,
452 struct libwebsocket *wsi,
453 enum libwebsocket_callback_reasons reason,
454 void *user, void *in, size_t len)
457 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
458 LWS_SEND_BUFFER_POST_PADDING];
459 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
460 struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
464 case LWS_CALLBACK_ESTABLISHED:
465 lwsl_info("callback_dumb_increment: "
466 "LWS_CALLBACK_ESTABLISHED\n");
470 case LWS_CALLBACK_SERVER_WRITEABLE:
471 n = sprintf((char *)p, "%d", pss->number++);
472 m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
474 lwsl_err("ERROR %d writing to di socket\n", n);
477 if (close_testing && pss->number == 50) {
478 lwsl_info("close tesing limit, closing\n");
483 case LWS_CALLBACK_RECEIVE:
484 // fprintf(stderr, "rx %d\n", (int)len);
487 if (strcmp((const char *)in, "reset\n") == 0)
491 * this just demonstrates how to use the protocol filter. If you won't
492 * study and reject connections based on header content, you don't need
493 * to handle this callback
496 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
497 dump_handshake_info(wsi);
498 /* you could return non-zero here and kill the connection */
509 /* lws-mirror_protocol */
511 #define MAX_MESSAGE_QUEUE 32
513 struct per_session_data__lws_mirror {
514 struct libwebsocket *wsi;
523 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
524 static int ringbuffer_head;
527 callback_lws_mirror(struct libwebsocket_context *context,
528 struct libwebsocket *wsi,
529 enum libwebsocket_callback_reasons reason,
530 void *user, void *in, size_t len)
533 struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
537 case LWS_CALLBACK_ESTABLISHED:
538 lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
539 pss->ringbuffer_tail = ringbuffer_head;
543 case LWS_CALLBACK_PROTOCOL_DESTROY:
544 lwsl_notice("mirror protocol cleaning up\n");
545 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
546 if (ringbuffer[n].payload)
547 free(ringbuffer[n].payload);
550 case LWS_CALLBACK_SERVER_WRITEABLE:
553 while (pss->ringbuffer_tail != ringbuffer_head) {
555 n = libwebsocket_write(wsi, (unsigned char *)
556 ringbuffer[pss->ringbuffer_tail].payload +
557 LWS_SEND_BUFFER_PRE_PADDING,
558 ringbuffer[pss->ringbuffer_tail].len,
560 if (n < ringbuffer[pss->ringbuffer_tail].len) {
561 lwsl_err("ERROR %d writing to mirror socket\n", n);
564 if (n < ringbuffer[pss->ringbuffer_tail].len)
565 lwsl_err("mirror partial write %d vs %d\n",
566 n, ringbuffer[pss->ringbuffer_tail].len);
568 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
569 pss->ringbuffer_tail = 0;
571 pss->ringbuffer_tail++;
573 if (((ringbuffer_head - pss->ringbuffer_tail) &
574 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
575 libwebsocket_rx_flow_allow_all_protocol(
576 libwebsockets_get_protocol(wsi));
578 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
580 if (lws_send_pipe_choked(wsi)) {
581 libwebsocket_callback_on_writable(context, wsi);
585 * for tests with chrome on same machine as client and
586 * server, this is needed to stop chrome choking
592 case LWS_CALLBACK_RECEIVE:
594 if (((ringbuffer_head - pss->ringbuffer_tail) &
595 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
596 lwsl_err("dropping!\n");
600 if (ringbuffer[ringbuffer_head].payload)
601 free(ringbuffer[ringbuffer_head].payload);
603 ringbuffer[ringbuffer_head].payload =
604 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
605 LWS_SEND_BUFFER_POST_PADDING);
606 ringbuffer[ringbuffer_head].len = len;
607 memcpy((char *)ringbuffer[ringbuffer_head].payload +
608 LWS_SEND_BUFFER_PRE_PADDING, in, len);
609 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
614 if (((ringbuffer_head - pss->ringbuffer_tail) &
615 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
619 lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
620 libwebsocket_rx_flow_control(wsi, 0);
622 // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
624 libwebsocket_callback_on_writable_all_protocol(
625 libwebsockets_get_protocol(wsi));
629 * this just demonstrates how to use the protocol filter. If you won't
630 * study and reject connections based on header content, you don't need
631 * to handle this callback
634 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
635 dump_handshake_info(wsi);
636 /* you could return non-zero here and kill the connection */
647 /* list of supported protocols and callbacks */
649 static struct libwebsocket_protocols protocols[] = {
650 /* first protocol must always be HTTP handler */
653 "http-only", /* name */
654 callback_http, /* callback */
655 sizeof (struct per_session_data__http), /* per_session_data_size */
656 0, /* max frame size / rx buffer */
659 "dumb-increment-protocol",
660 callback_dumb_increment,
661 sizeof(struct per_session_data__dumb_increment),
665 "lws-mirror-protocol",
667 sizeof(struct per_session_data__lws_mirror),
670 { NULL, NULL, 0, 0 } /* terminator */
673 void sighandler(int sig)
678 static struct option options[] = {
679 { "help", no_argument, NULL, 'h' },
680 { "debug", required_argument, NULL, 'd' },
681 { "port", required_argument, NULL, 'p' },
682 { "ssl", no_argument, NULL, 's' },
683 { "interface", required_argument, NULL, 'i' },
684 { "closetest", no_argument, NULL, 'c' },
685 #ifndef LWS_NO_DAEMONIZE
686 { "daemonize", no_argument, NULL, 'D' },
688 { "resource_path", required_argument, NULL, 'r' },
692 int main(int argc, char **argv)
694 char cert_path[1024];
698 struct libwebsocket_context *context;
700 char interface_name[128] = "";
701 const char *iface = NULL;
703 int syslog_options = LOG_PID | LOG_PERROR;
705 unsigned int oldus = 0;
706 struct lws_context_creation_info info;
709 #ifndef LWS_NO_DAEMONIZE
713 memset(&info, 0, sizeof info);
717 n = getopt_long(argc, argv, "ci:hsp:d:Dr:", options, NULL);
721 #ifndef LWS_NO_DAEMONIZE
725 syslog_options &= ~LOG_PERROR;
730 debug_level = atoi(optarg);
736 info.port = atoi(optarg);
739 strncpy(interface_name, optarg, sizeof interface_name);
740 interface_name[(sizeof interface_name) - 1] = '\0';
741 iface = interface_name;
745 fprintf(stderr, " Close testing mode -- closes on "
746 "client after 50 dumb increments"
747 "and suppresses lws_mirror spam\n");
750 resource_path = optarg;
751 printf("Setting resource path to \"%s\"\n", resource_path);
754 fprintf(stderr, "Usage: test-server "
755 "[--port=<p>] [--ssl] "
756 "[-d <log bitfield>] "
757 "[--resource_path <path>]\n");
762 #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
764 * normally lock path would be /var/lock/lwsts or similar, to
765 * simplify getting started without having to take care about
766 * permissions or running as root, set to /tmp/.lwsts-lock
768 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
769 fprintf(stderr, "Failed to daemonize\n");
774 signal(SIGINT, sighandler);
777 /* we will only try to log things according to our debug_level */
778 setlogmask(LOG_UPTO (LOG_DEBUG));
779 openlog("lwsts", syslog_options, LOG_DAEMON);
782 /* tell the library what debug level to emit and to send it to syslog */
783 lws_set_log_level(debug_level, lwsl_emit_syslog);
785 lwsl_notice("libwebsockets test server - "
786 "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
787 "licensed under LGPL2.1\n");
789 max_poll_elements = getdtablesize();
790 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
791 fd_lookup = malloc(max_poll_elements * sizeof (int));
792 if (pollfds == NULL || fd_lookup == NULL) {
793 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
799 info.protocols = protocols;
800 #ifndef LWS_NO_EXTENSIONS
801 info.extensions = libwebsocket_get_internal_extensions();
804 info.ssl_cert_filepath = NULL;
805 info.ssl_private_key_filepath = NULL;
807 if (strlen(resource_path) > sizeof(cert_path) - 32) {
808 lwsl_err("resource path too long\n");
811 sprintf(cert_path, "%s/libwebsockets-test-server.pem",
813 if (strlen(resource_path) > sizeof(key_path) - 32) {
814 lwsl_err("resource path too long\n");
817 sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
820 info.ssl_cert_filepath = cert_path;
821 info.ssl_private_key_filepath = key_path;
827 context = libwebsocket_create_context(&info);
828 if (context == NULL) {
829 lwsl_err("libwebsocket init failed\n");
834 while (n >= 0 && !force_exit) {
837 gettimeofday(&tv, NULL);
840 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
841 * live websocket connection using the DUMB_INCREMENT protocol,
842 * as soon as it can take more packets (usually immediately)
845 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
846 libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
853 * this represents an existing server's single poll action
854 * which also includes libwebsocket sockets
857 n = poll(pollfds, count_pollfds, 50);
863 for (n = 0; n < count_pollfds; n++)
864 if (pollfds[n].revents)
866 * returns immediately if the fd does not
867 * match anything under libwebsockets
870 if (libwebsocket_service_fd(context,
875 * If libwebsockets sockets are all we care about,
876 * you can use this api which takes care of the poll()
877 * and looping through finding who needed service.
879 * If no socket needs service, it'll return anyway after
880 * the number of ms in the second argument.
883 n = libwebsocket_service(context, 50);
891 libwebsocket_context_destroy(context);
893 lwsl_notice("libwebsockets-test-server exited cleanly\n");