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"
93 * We take a strict whitelist approach to stop ../ attacks
101 static const struct serveable whitelist[] = {
102 { "/favicon.ico", "image/x-icon" },
103 { "/libwebsockets.org-logo.png", "image/png" },
105 /* last one is the default served if no match */
106 { "/test.html", "text/html" },
109 struct per_session_data__http {
113 /* this protocol server (always the first one) just knows how to do HTTP */
115 static int callback_http(struct libwebsocket_context *context,
116 struct libwebsocket *wsi,
117 enum libwebsocket_callback_reasons reason, void *user,
118 void *in, size_t len)
121 char client_name[128];
127 static unsigned char buffer[4096];
128 struct stat stat_buf;
129 struct per_session_data__http *pss = (struct per_session_data__http *)user;
131 int fd = (int)(long)in;
135 case LWS_CALLBACK_HTTP:
137 /* check for the "send a big file by hand" example case */
139 if (!strcmp((const char *)in, "/leaf.jpg")) {
141 /* well, let's demonstrate how to send the hard way */
146 pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY | _O_BINARY);
148 pss->fd = open(LOCAL_RESOURCE_PATH"/leaf.jpg", O_RDONLY);
154 fstat(pss->fd, &stat_buf);
157 * we will send a big jpeg file, but it could be
158 * anything. Set the Content-Type: appropriately
159 * so the browser knows what to do with it.
162 p += sprintf((char *)p,
163 "HTTP/1.0 200 OK\x0d\x0a"
164 "Server: libwebsockets\x0d\x0a"
165 "Content-Type: image/jpeg\x0d\x0a"
166 "Content-Length: %u\x0d\x0a\x0d\x0a",
167 (unsigned int)stat_buf.st_size);
170 * send the http headers...
171 * this won't block since it's the first payload sent
172 * on the connection since it was established
173 * (too small for partial)
176 n = libwebsocket_write(wsi, buffer,
177 p - buffer, LWS_WRITE_HTTP);
184 * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
186 libwebsocket_callback_on_writable(context, wsi);
190 /* if not, send a file the easy way */
192 for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
193 if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
196 sprintf(buf, LOCAL_RESOURCE_PATH"%s", whitelist[n].urlpath);
198 if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
199 return -1; /* through completion or error, close the socket */
202 * notice that the sending of the file completes asynchronously,
203 * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
209 case LWS_CALLBACK_HTTP_FILE_COMPLETION:
210 // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
211 /* kill the connection after we sent one file */
214 case LWS_CALLBACK_HTTP_WRITEABLE:
216 * we can send more of whatever it is we were sending
220 n = read(pss->fd, buffer, sizeof buffer);
221 /* problem reading, close conn */
224 /* sent it all, close conn */
228 * because it's HTTP and not websocket, don't need to take
229 * care about pre and postamble
231 m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
233 /* write failed, close conn */
236 /* partial write, adjust */
237 lseek(pss->fd, m - n, SEEK_CUR);
239 } while (!lws_send_pipe_choked(wsi));
240 libwebsocket_callback_on_writable(context, wsi);
248 * callback for confirming to continue with client IP appear in
249 * protocol 0 callback since no websocket protocol has been agreed
250 * yet. You can just ignore this if you won't filter on client IP
251 * since the default uhandled callback return is 0 meaning let the
252 * connection continue.
255 case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
257 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
258 sizeof(client_name), client_ip, sizeof(client_ip));
260 fprintf(stderr, "Received network connect from %s (%s)\n",
261 client_name, client_ip);
263 /* if we returned non-zero from here, we kill the connection */
268 * callbacks for managing the external poll() array appear in
269 * protocol 0 callback
272 case LWS_CALLBACK_ADD_POLL_FD:
274 if (count_pollfds >= max_poll_elements) {
275 lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
279 fd_lookup[fd] = count_pollfds;
280 pollfds[count_pollfds].fd = fd;
281 pollfds[count_pollfds].events = (int)(long)len;
282 pollfds[count_pollfds++].revents = 0;
285 case LWS_CALLBACK_DEL_POLL_FD:
286 if (!--count_pollfds)
289 /* have the last guy take up the vacant slot */
290 pollfds[m] = pollfds[count_pollfds];
291 fd_lookup[pollfds[count_pollfds].fd] = m;
294 case LWS_CALLBACK_SET_MODE_POLL_FD:
295 pollfds[fd_lookup[fd]].events |= (int)(long)len;
298 case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
299 pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
311 * this is just an example of parsing handshake headers, you don't need this
312 * in your code unless you will filter allowing connections by the header
317 dump_handshake_info(struct libwebsocket *wsi)
320 static const char *token_names[WSI_TOKEN_COUNT] = {
321 /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
322 /*[WSI_TOKEN_HOST] =*/ "Host",
323 /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
324 /*[WSI_TOKEN_KEY1] =*/ "key 1",
325 /*[WSI_TOKEN_KEY2] =*/ "key 2",
326 /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
327 /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
328 /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
329 /*[WSI_TOKEN_DRAFT] =*/ "Draft",
330 /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
333 /*[WSI_TOKEN_KEY] =*/ "Key",
334 /*[WSI_TOKEN_VERSION] =*/ "Version",
335 /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
338 /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
340 /* client receives these */
341 /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
342 /*[WSI_TOKEN_NONCE] =*/ "Nonce",
343 /*[WSI_TOKEN_HTTP] =*/ "Http",
344 /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
348 for (n = 0; n < WSI_TOKEN_COUNT; n++) {
349 if (!lws_hdr_total_length(wsi, n))
352 lws_hdr_copy(wsi, buf, sizeof buf, n);
354 fprintf(stderr, " %s = %s\n", token_names[n], buf);
358 /* dumb_increment protocol */
361 * one of these is auto-created for each connection and a pointer to the
362 * appropriate instance is passed to the callback in the user parameter
364 * for this example protocol we use it to individualize the count for each
368 struct per_session_data__dumb_increment {
373 callback_dumb_increment(struct libwebsocket_context *context,
374 struct libwebsocket *wsi,
375 enum libwebsocket_callback_reasons reason,
376 void *user, void *in, size_t len)
379 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
380 LWS_SEND_BUFFER_POST_PADDING];
381 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
382 struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
386 case LWS_CALLBACK_ESTABLISHED:
387 lwsl_info("callback_dumb_increment: "
388 "LWS_CALLBACK_ESTABLISHED\n");
392 case LWS_CALLBACK_SERVER_WRITEABLE:
393 n = sprintf((char *)p, "%d", pss->number++);
394 /* too small for partial */
395 n = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
397 lwsl_err("ERROR %d writing to di socket\n", n);
400 if (close_testing && pss->number == 50) {
401 lwsl_info("close tesing limit, closing\n");
406 case LWS_CALLBACK_RECEIVE:
407 // fprintf(stderr, "rx %d\n", (int)len);
410 if (strcmp((const char *)in, "reset\n") == 0)
414 * this just demonstrates how to use the protocol filter. If you won't
415 * study and reject connections based on header content, you don't need
416 * to handle this callback
419 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
420 dump_handshake_info(wsi);
421 /* you could return non-zero here and kill the connection */
432 /* lws-mirror_protocol */
434 #define MAX_MESSAGE_QUEUE 128
436 struct per_session_data__lws_mirror {
437 struct libwebsocket *wsi;
446 static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
447 static int ringbuffer_head;
449 static struct libwebsocket *wsi_choked[20];
450 static int num_wsi_choked;
453 callback_lws_mirror(struct libwebsocket_context *context,
454 struct libwebsocket *wsi,
455 enum libwebsocket_callback_reasons reason,
456 void *user, void *in, size_t len)
459 struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
463 case LWS_CALLBACK_ESTABLISHED:
464 lwsl_info("callback_lws_mirror: "
465 "LWS_CALLBACK_ESTABLISHED\n");
466 pss->ringbuffer_tail = ringbuffer_head;
470 case LWS_CALLBACK_PROTOCOL_DESTROY:
471 lwsl_notice("mirror protocol cleaning up\n");
472 for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
473 if (ringbuffer[n].payload)
474 free(ringbuffer[n].payload);
477 case LWS_CALLBACK_SERVER_WRITEABLE:
480 while (pss->ringbuffer_tail != ringbuffer_head) {
482 n = libwebsocket_write(wsi, (unsigned char *)
483 ringbuffer[pss->ringbuffer_tail].payload +
484 LWS_SEND_BUFFER_PRE_PADDING,
485 ringbuffer[pss->ringbuffer_tail].len,
488 lwsl_err("ERROR %d writing to mirror socket\n", n);
491 if (n < ringbuffer[pss->ringbuffer_tail].len) {
492 lwsl_err("mirror partial write %d vs %d\n", n, ringbuffer[pss->ringbuffer_tail].len);
495 if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
496 pss->ringbuffer_tail = 0;
498 pss->ringbuffer_tail++;
500 if (((ringbuffer_head - pss->ringbuffer_tail) &
501 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) {
502 for (n = 0; n < num_wsi_choked; n++)
503 libwebsocket_rx_flow_control(wsi_choked[n], 1);
506 // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
508 if (lws_send_pipe_choked(wsi)) {
509 libwebsocket_callback_on_writable(context, wsi);
515 case LWS_CALLBACK_RECEIVE:
517 if (((ringbuffer_head - pss->ringbuffer_tail) &
518 (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
519 lwsl_err("dropping!\n");
523 if (ringbuffer[ringbuffer_head].payload)
524 free(ringbuffer[ringbuffer_head].payload);
526 ringbuffer[ringbuffer_head].payload =
527 malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
528 LWS_SEND_BUFFER_POST_PADDING);
529 ringbuffer[ringbuffer_head].len = len;
530 memcpy((char *)ringbuffer[ringbuffer_head].payload +
531 LWS_SEND_BUFFER_PRE_PADDING, in, len);
532 if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
537 if (((ringbuffer_head - pss->ringbuffer_tail) &
538 (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
542 if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) {
543 libwebsocket_rx_flow_control(wsi, 0);
544 wsi_choked[num_wsi_choked++] = wsi;
547 // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
549 libwebsocket_callback_on_writable_all_protocol(
550 libwebsockets_get_protocol(wsi));
554 * this just demonstrates how to use the protocol filter. If you won't
555 * study and reject connections based on header content, you don't need
556 * to handle this callback
559 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
560 dump_handshake_info(wsi);
561 /* you could return non-zero here and kill the connection */
572 /* list of supported protocols and callbacks */
574 static struct libwebsocket_protocols protocols[] = {
575 /* first protocol must always be HTTP handler */
578 "http-only", /* name */
579 callback_http, /* callback */
580 sizeof (struct per_session_data__http), /* per_session_data_size */
581 0, /* max frame size / rx buffer */
584 "dumb-increment-protocol",
585 callback_dumb_increment,
586 sizeof(struct per_session_data__dumb_increment),
590 "lws-mirror-protocol",
592 sizeof(struct per_session_data__lws_mirror),
595 { NULL, NULL, 0, 0 } /* terminator */
598 void sighandler(int sig)
603 static struct option options[] = {
604 { "help", no_argument, NULL, 'h' },
605 { "debug", required_argument, NULL, 'd' },
606 { "port", required_argument, NULL, 'p' },
607 { "ssl", no_argument, NULL, 's' },
608 { "interface", required_argument, NULL, 'i' },
609 { "closetest", no_argument, NULL, 'c' },
610 #ifndef LWS_NO_DAEMONIZE
611 { "daemonize", no_argument, NULL, 'D' },
616 int main(int argc, char **argv)
620 struct libwebsocket_context *context;
622 char interface_name[128] = "";
623 const char *iface = NULL;
625 int syslog_options = LOG_PID | LOG_PERROR;
627 unsigned int oldus = 0;
628 struct lws_context_creation_info info;
631 #ifndef LWS_NO_DAEMONIZE
635 memset(&info, 0, sizeof info);
639 n = getopt_long(argc, argv, "ci:hsp:d:D", options, NULL);
643 #ifndef LWS_NO_DAEMONIZE
647 syslog_options &= ~LOG_PERROR;
652 debug_level = atoi(optarg);
658 info.port = atoi(optarg);
661 strncpy(interface_name, optarg, sizeof interface_name);
662 interface_name[(sizeof interface_name) - 1] = '\0';
663 iface = interface_name;
667 fprintf(stderr, " Close testing mode -- closes on "
668 "client after 50 dumb increments"
669 "and suppresses lws_mirror spam\n");
672 fprintf(stderr, "Usage: test-server "
673 "[--port=<p>] [--ssl] "
674 "[-d <log bitfield>]\n");
679 #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
681 * normally lock path would be /var/lock/lwsts or similar, to
682 * simplify getting started without having to take care about
683 * permissions or running as root, set to /tmp/.lwsts-lock
685 if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
686 fprintf(stderr, "Failed to daemonize\n");
691 signal(SIGINT, sighandler);
694 /* we will only try to log things according to our debug_level */
695 setlogmask(LOG_UPTO (LOG_DEBUG));
696 openlog("lwsts", syslog_options, LOG_DAEMON);
699 /* tell the library what debug level to emit and to send it to syslog */
700 lws_set_log_level(debug_level, lwsl_emit_syslog);
702 lwsl_notice("libwebsockets test server - "
703 "(C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - "
704 "licensed under LGPL2.1\n");
706 max_poll_elements = getdtablesize();
707 pollfds = malloc(max_poll_elements * sizeof (struct pollfd));
708 fd_lookup = malloc(max_poll_elements * sizeof (int));
709 if (pollfds == NULL || fd_lookup == NULL) {
710 lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
716 info.protocols = protocols;
717 #ifndef LWS_NO_EXTENSIONS
718 info.extensions = libwebsocket_get_internal_extensions();
721 info.ssl_cert_filepath = NULL;
722 info.ssl_private_key_filepath = NULL;
724 info.ssl_cert_filepath = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.pem";
725 info.ssl_private_key_filepath = LOCAL_RESOURCE_PATH"/libwebsockets-test-server.key.pem";
731 context = libwebsocket_create_context(&info);
732 if (context == NULL) {
733 lwsl_err("libwebsocket init failed\n");
738 while (n >= 0 && !force_exit) {
741 gettimeofday(&tv, NULL);
744 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
745 * live websocket connection using the DUMB_INCREMENT protocol,
746 * as soon as it can take more packets (usually immediately)
749 if (((unsigned int)tv.tv_usec - oldus) > 50000) {
750 libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]);
757 * this represents an existing server's single poll action
758 * which also includes libwebsocket sockets
761 n = poll(pollfds, count_pollfds, 50);
767 for (n = 0; n < count_pollfds; n++)
768 if (pollfds[n].revents)
770 * returns immediately if the fd does not
771 * match anything under libwebsockets
774 if (libwebsocket_service_fd(context,
779 * If libwebsockets sockets are all we care about,
780 * you can use this api which takes care of the poll()
781 * and looping through finding who needed service.
783 * If no socket needs service, it'll return anyway after
784 * the number of ms in the second argument.
787 n = libwebsocket_service(context, 50);
795 libwebsocket_context_destroy(context);
797 lwsl_notice("libwebsockets-test-server exited cleanly\n");