• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets-test-server - libwebsockets test implementation
3  *
4  * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * The person who associated a work with this deed has dedicated
10  * the work to the public domain by waiving all of his or her rights
11  * to the work worldwide under copyright law, including all related
12  * and neighboring rights, to the extent allowed by law. You can copy,
13  * modify, distribute and perform the work, even for commercial purposes,
14  * all without asking permission.
15  *
16  * The test apps are intended to be adapted for use in your code, which
17  * may be proprietary.	So unlike the library itself, they are licensed
18  * Public Domain.
19  */
20 
21 #include <libwebsockets.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
25 #include <getopt.h>
26 #endif
27 #include <signal.h>
28 
29 #if defined(WIN32) || defined(_WIN32)
30 #else
31 #include <unistd.h>
32 #endif
33 
34 int close_testing;
35 int max_poll_elements;
36 int debug_level = LLL_USER | 7;
37 
38 #if defined(LWS_WITH_EXTERNAL_POLL)
39 struct lws_pollfd *pollfds;
40 int *fd_lookup;
41 int count_pollfds;
42 #endif
43 volatile int force_exit = 0, dynamic_vhost_enable = 0;
44 struct lws_vhost *dynamic_vhost;
45 struct lws_context *context;
46 struct lws_plat_file_ops fops_plat;
47 static int test_options;
48 
49 /* http server gets files from this path */
50 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
51 char *resource_path = LOCAL_RESOURCE_PATH;
52 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
53 char crl_path[1024] = "";
54 #endif
55 
56 /*
57  * This demonstrates how to use the clean protocol service separation of
58  * plugins, in this case by statically including them at build-time.
59  */
60 
61 #define LWS_PLUGIN_STATIC
62 #if defined(LWS_ROLE_WS)
63 #include "../plugins/protocol_lws_mirror.c"
64 #include "../plugins/protocol_lws_status.c"
65 #include "../plugins/protocol_dumb_increment.c"
66 #endif
67 #include "../plugins/protocol_post_demo.c"
68 
69 #if defined(LWS_WITH_EXTERNAL_POLL)
70 static struct lws_pollfd *
ext_find_fd(lws_sockfd_type fd)71 ext_find_fd(lws_sockfd_type fd)
72 {
73 	int n;
74 
75 	for (n = 0; n < max_poll_elements; n++)
76 		if (pollfds[n].fd == fd)
77 			return &pollfds[n];
78 
79 	return NULL;
80 }
81 #endif
82 
83 static int
lws_callback_http(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)84 lws_callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
85 		  void *in, size_t len)
86 {
87 	const unsigned char *c;
88 #if defined(LWS_WITH_EXTERNAL_POLL)
89 	struct lws_pollargs *pa;
90 	struct lws_pollfd *pfd;
91 #endif
92 	char buf[1024];
93 	int n = 0, hlen;
94 
95 	switch (reason) {
96 #if defined(LWS_WITH_EXTERNAL_POLL)
97 	case LWS_CALLBACK_ADD_POLL_FD:
98 		pa = (struct lws_pollargs *)in;
99 		lwsl_debug("%s: ADD fd %d, ev %d\n", __func__, pa->fd, pa->events);
100 		pfd = ext_find_fd(pa->fd);
101 		if (pfd) {
102 			lwsl_notice("%s: ADD fd %d already in ext table\n",
103 					__func__, pa->fd);
104 		} else {
105 			pfd = ext_find_fd(LWS_SOCK_INVALID);
106 			if (!pfd)
107 				return -1;
108 		}
109 		pfd->fd = pa->fd;
110 		pfd->events = (short)pa->events;
111 		pfd->revents = 0;
112 		/* high water mark... */
113 		count_pollfds = (int)((pfd - pollfds) + 1);
114 		break;
115 	case LWS_CALLBACK_DEL_POLL_FD:
116 		pa = (struct lws_pollargs *)in;
117 		lwsl_debug("%s: DEL fd %d\n", __func__, pa->fd);
118 		pfd = ext_find_fd(pa->fd);
119 		if (!pfd)
120 			return -1;
121 		pfd->fd = LWS_SOCK_INVALID;
122 		break;
123 	case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
124 		pa = (struct lws_pollargs *)in;
125 		lwsl_debug("%s: CH fd %d\n", __func__, pa->fd);
126 		pfd = ext_find_fd(pa->fd);
127 		if (!pfd) {
128 			lwsl_err("%s: unknown fd %d\n", __func__, pa->fd);
129 			return -1;
130 		}
131 		pfd->events = (short)pa->events;
132 		break;
133 #endif
134 	case LWS_CALLBACK_HTTP:
135 
136 		/* non-mount-handled accesses will turn up here */
137 
138 		/* dump the headers */
139 
140 		do {
141 			c = lws_token_to_string((enum lws_token_indexes)n);
142 			if (!c) {
143 				n++;
144 				continue;
145 			}
146 
147 			hlen = lws_hdr_total_length(wsi, (enum lws_token_indexes)n);
148 			if (!hlen || hlen > (int)sizeof(buf) - 1) {
149 				n++;
150 				continue;
151 			}
152 
153 			if (lws_hdr_copy(wsi, buf, sizeof buf, (enum lws_token_indexes)n) < 0)
154 				fprintf(stderr, "    %s (too big)\n", (char *)c);
155 			else {
156 				buf[sizeof(buf) - 1] = '\0';
157 
158 				fprintf(stderr, "    %s = %s\n", (char *)c, buf);
159 			}
160 			n++;
161 		} while (c);
162 
163 		/* dump the individual URI Arg parameters */
164 
165 		n = 0;
166 		while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf),
167 					     WSI_TOKEN_HTTP_URI_ARGS, n) > 0) {
168 			lwsl_notice("URI Arg %d: %s\n", ++n, buf);
169 		}
170 
171 		if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL))
172 			return -1;
173 
174 		if (lws_http_transaction_completed(wsi))
175 			return -1;
176 
177 		return 0;
178 	default:
179 		break;
180 	}
181 
182 	return lws_callback_http_dummy(wsi, reason, user, in, len);
183 }
184 
185 /* list of supported protocols and callbacks */
186 
187 static struct lws_protocols protocols[] = {
188 	/* first protocol must always be HTTP handler */
189 
190 	{ "http-only", lws_callback_http, 0, 0, 0, NULL, 0 },
191 #if defined(LWS_ROLE_WS)
192 	LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT,
193 	LWS_PLUGIN_PROTOCOL_MIRROR,
194 	LWS_PLUGIN_PROTOCOL_LWS_STATUS,
195 #endif
196 	LWS_PLUGIN_PROTOCOL_POST_DEMO,
197 	LWS_PROTOCOL_LIST_TERM
198 };
199 
200 
201 /* this shows how to override the lws file operations.	You don't need
202  * to do any of this unless you have a reason (eg, want to serve
203  * compressed files without decompressing the whole archive)
204  */
205 static lws_fop_fd_t
test_server_fops_open(const struct lws_plat_file_ops * fops,const char * vfs_path,const char * vpath,lws_fop_flags_t * flags)206 test_server_fops_open(const struct lws_plat_file_ops *fops,
207 		     const char *vfs_path, const char *vpath,
208 		     lws_fop_flags_t *flags)
209 {
210 	lws_fop_fd_t fop_fd;
211 
212 	/* call through to original platform implementation */
213 	fop_fd = fops_plat.open(fops, vfs_path, vpath, flags);
214 
215 	if (fop_fd)
216 		lwsl_info("%s: opening %s, ret %p, len %lu\n", __func__,
217 				vfs_path, fop_fd,
218 				(long)lws_vfs_get_length(fop_fd));
219 	else
220 		lwsl_info("%s: open %s failed\n", __func__, vfs_path);
221 
222 	return fop_fd;
223 }
224 
sighandler(int sig)225 void sighandler(int sig)
226 {
227 #if !defined(WIN32) && !defined(_WIN32)
228 	/* because windows is too dumb to have SIGUSR1... */
229 	if (sig == SIGUSR1) {
230 		/*
231 		 * For testing, you can fire a SIGUSR1 at the test server
232 		 * to toggle the existence of an identical server on
233 		 * port + 1
234 		 */
235 		dynamic_vhost_enable ^= 1;
236 		lws_cancel_service(context);
237 		lwsl_notice("SIGUSR1: dynamic_vhost_enable: %d\n",
238 				dynamic_vhost_enable);
239 		return;
240 	}
241 #endif
242 	force_exit = 1;
243 	lws_cancel_service(context);
244 }
245 
246 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
247 static const struct lws_extension exts[] = {
248 	{
249 		"permessage-deflate",
250 		lws_extension_callback_pm_deflate,
251 		"permessage-deflate"
252 	},
253 	{ NULL, NULL, NULL /* terminator */ }
254 };
255 #endif
256 
257 /*
258  * mount a filesystem directory into the URL space at /
259  * point it to our /usr/share directory with our assets in
260  * stuff from here is autoserved by the library
261  */
262 
263 static const struct lws_http_mount mount_ziptest_uncomm = {
264 	NULL,			/* linked-list pointer to next*/
265 	"/uncommziptest",		/* mountpoint in URL namespace on this vhost */
266 	LOCAL_RESOURCE_PATH"/candide-uncompressed.zip",	/* handler */
267 	NULL,	/* default filename if none given */
268 	NULL,
269 	NULL,
270 	NULL,
271 	NULL,
272 	0,
273 	0,
274 	0,
275 	0,
276 	0,
277 	0,
278 	LWSMPRO_FILE,	/* origin points to a callback */
279 	14,			/* strlen("/ziptest"), ie length of the mountpoint */
280 	NULL,
281 }, mount_ziptest = {
282 	(struct lws_http_mount *)&mount_ziptest_uncomm,			/* linked-list pointer to next*/
283 	"/ziptest",		/* mountpoint in URL namespace on this vhost */
284 	LOCAL_RESOURCE_PATH"/candide.zip",	/* handler */
285 	NULL,	/* default filename if none given */
286 	NULL,
287 	NULL,
288 	NULL,
289 	NULL,
290 	0,
291 	0,
292 	0,
293 	0,
294 	0,
295 	0,
296 	LWSMPRO_FILE,	/* origin points to a callback */
297 	8,			/* strlen("/ziptest"), ie length of the mountpoint */
298 	NULL,
299 }, mount_post = {
300 	(struct lws_http_mount *)&mount_ziptest, /* linked-list pointer to next*/
301 	"/formtest",		/* mountpoint in URL namespace on this vhost */
302 	"protocol-post-demo",	/* handler */
303 	NULL,	/* default filename if none given */
304 	NULL,
305 	NULL,
306 	NULL,
307 	NULL,
308 	0,
309 	0,
310 	0,
311 	0,
312 	0,
313 	0,
314 	LWSMPRO_CALLBACK,	/* origin points to a callback */
315 	9,			/* strlen("/formtest"), ie length of the mountpoint */
316 	NULL,
317 }, mount = {
318 	/* .mount_next */		&mount_post,	/* linked-list "next" */
319 	/* .mountpoint */		"/",		/* mountpoint URL */
320 	/* .origin */			LOCAL_RESOURCE_PATH, /* serve from dir */
321 	/* .def */			"test.html",	/* default filename */
322 	/* .protocol */			NULL,
323 	/* .cgienv */			NULL,
324 	/* .extra_mimetypes */		NULL,
325 	/* .interpret */		NULL,
326 	/* .cgi_timeout */		0,
327 	/* .cache_max_age */		0,
328 	/* .auth_mask */		0,
329 	/* .cache_reusable */		0,
330 	/* .cache_revalidate */		0,
331 	/* .cache_intermediaries */	0,
332 	/* .origin_protocol */		LWSMPRO_FILE,	/* files in a dir */
333 	/* .mountpoint_len */		1,		/* char count */
334 	/* .basic_auth_login_file */	NULL,
335 };
336 
337 
338 static const struct lws_protocol_vhost_options pvo_options = {
339 	NULL,
340 	NULL,
341 	"options",		/* pvo name */
342 	(void *)&test_options	/* pvo value */
343 };
344 
345 /*
346  * If we don't give any pvos, then for backwards compatibility all protocols
347  * are enabled on all vhosts.  If we give any pvos, then we must list in them
348  * the protocol names we want to enable, protocols that are not listed in the
349  * pvos are not instantiated on the vhost then.
350  */
351 
352 static const struct lws_protocol_vhost_options
353 	pvo3 = {
354 		NULL,				/* "next" pvo linked-list */
355 		NULL,
356 		"protocol-post-demo",	/* protocol name we belong to on this vhost */
357 		""				/* not needed */
358 	},
359 	pvo2 = {
360 		&pvo3,				/* "next" pvo linked-list */
361 		NULL,
362 		"lws-status",	/* protocol name we belong to on this vhost */
363 		""				/* not needed */
364 	},
365 	pvo1 = {
366 		&pvo2,				/* "next" pvo linked-list */
367 		NULL,
368 		"lws-mirror-protocol",	/* protocol name we belong to on this vhost */
369 		""				/* not needed */
370 	},
371 	pvo = {
372 		&pvo1,				/* "next" pvo linked-list */
373 		&pvo_options,		/* "child" pvo linked-list */
374 		"dumb-increment-protocol",	/* protocol name we belong to on this vhost */
375 		""				/* not needed */
376 };
377 
378 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
379 static struct option options[] = {
380 	{ "help",	no_argument,		NULL, 'h' },
381 	{ "debug",	required_argument,	NULL, 'd' },
382 	{ "port",	required_argument,	NULL, 'p' },
383 	{ "ssl",	no_argument,		NULL, 's' },
384 	{ "allow-non-ssl",	no_argument,	NULL, 'a' },
385 	{ "interface",	required_argument,	NULL, 'i' },
386 	{ "closetest",	no_argument,		NULL, 'c' },
387 	{ "ssl-cert",  required_argument,	NULL, 'C' },
388 	{ "ssl-key",  required_argument,	NULL, 'K' },
389 	{ "ssl-ca",  required_argument,		NULL, 'A' },
390 	{ "resource-path",  required_argument,		NULL, 'r' },
391 #if defined(LWS_WITH_TLS)
392 	{ "ssl-verify-client",	no_argument,		NULL, 'v' },
393 #if defined(LWS_HAVE_SSL_CTX_set1_param)
394 	{ "ssl-crl",  required_argument,		NULL, 'R' },
395 #endif
396 #endif
397 	{ "libev",  no_argument,		NULL, 'e' },
398 	{ "unix-socket",  required_argument,	NULL, 'U' },
399 #ifndef LWS_NO_DAEMONIZE
400 	{ "daemonize",	no_argument,		NULL, 'D' },
401 #endif
402 	{ "ignore-sigterm", no_argument,	NULL, 'I' },
403 
404 	{ NULL, 0, 0, 0 }
405 };
406 #endif
407 
408 static void
sigterm_catch(int sig)409 sigterm_catch(int sig)
410 {
411 }
412 
main(int argc,char ** argv)413 int main(int argc, char **argv)
414 {
415 	struct lws_context_creation_info info;
416 	struct lws_vhost *vhost;
417 	char interface_name[128] = "";
418 	const char *iface = NULL;
419 	char cert_path[1024] = "";
420 	char key_path[1024] = "";
421 	char ca_path[1024] = "";
422 #ifndef LWS_NO_DAEMONIZE
423 	int daemonize = 0;
424 #endif
425 	uint64_t opts = 0;
426 	int use_ssl = 0;
427 	uid_t uid = (uid_t)-1;
428 	gid_t gid = (gid_t)-1;
429 	int n = 0;
430 
431 	/*
432 	 * take care to zero down the info struct, he contains random garbaage
433 	 * from the stack otherwise
434 	 */
435 	memset(&info, 0, sizeof info);
436 	info.port = 7681;
437 
438 	while (n >= 0) {
439 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
440 		n = getopt_long(argc, argv, "eci:hsap:d:DC:K:A:R:vu:g:kU:niIr:", options, NULL);
441 #else
442 		n = getopt(argc, argv, "eci:hsap:d:DC:K:A:R:vu:g:kU:nIr:");
443 #endif
444 		if (n < 0)
445 			continue;
446 		switch (n) {
447 		case 'e':
448 			opts |= LWS_SERVER_OPTION_LIBEV;
449 			break;
450 #ifndef LWS_NO_DAEMONIZE
451 		case 'D':
452 			daemonize = 1;
453 			break;
454 #endif
455 		case 'u':
456 			uid = (uid_t)atoi(optarg);
457 			break;
458 		case 'g':
459 			gid = (gid_t)atoi(optarg);
460 			break;
461 		case 'd':
462 			debug_level = atoi(optarg);
463 			break;
464 		case 'n':
465 			/* no dumb increment send */
466 			test_options |= 1;
467 			break;
468 		case 'I':
469 			signal(SIGTERM, sigterm_catch);
470 			break;
471 		case 'r':
472 			resource_path = optarg;
473 			break;
474 		case 's':
475 			use_ssl = 1;
476 			opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
477 			break;
478 		case 'a':
479 			opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
480 			break;
481 		case 'p':
482 			info.port = atoi(optarg);
483 			break;
484 		case 'i':
485 			lws_strncpy(interface_name, optarg, sizeof interface_name);
486 			iface = interface_name;
487 			break;
488 		case 'U':
489 			lws_strncpy(interface_name, optarg, sizeof interface_name);
490 			iface = interface_name;
491 			opts |= LWS_SERVER_OPTION_UNIX_SOCK;
492 			break;
493 		case 'k':
494 			info.bind_iface = 1;
495 #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
496 			info.caps[0] = CAP_NET_RAW;
497 			info.count_caps = 1;
498 #endif
499 			break;
500 		case 'c':
501 			close_testing = 1;
502 			fprintf(stderr, " Close testing mode -- closes on "
503 					   "client after 50 dumb increments"
504 					   "and suppresses lws_mirror spam\n");
505 			break;
506 		case 'C':
507 			lws_strncpy(cert_path, optarg, sizeof(cert_path));
508 			break;
509 		case 'K':
510 			lws_strncpy(key_path, optarg, sizeof(key_path));
511 			break;
512 		case 'A':
513 			lws_strncpy(ca_path, optarg, sizeof(ca_path));
514 			break;
515 #if defined(LWS_WITH_TLS)
516 		case 'v':
517 			use_ssl = 1;
518 			opts |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
519 			break;
520 
521 #if defined(LWS_HAVE_SSL_CTX_set1_param)
522 		case 'R':
523 			lws_strncpy(crl_path, optarg, sizeof(crl_path));
524 			break;
525 #endif
526 #endif
527 		case 'h':
528 			fprintf(stderr, "Usage: test-server "
529 					"[--port=<p>] [--ssl] "
530 					"[-d <log bitfield>]\n");
531 			exit(1);
532 		}
533 	}
534 
535 #if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
536 	/*
537 	 * normally lock path would be /var/lock/lwsts or similar, to
538 	 * simplify getting started without having to take care about
539 	 * permissions or running as root, set to /tmp/.lwsts-lock
540 	 */
541 	if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
542 		fprintf(stderr, "Failed to daemonize\n");
543 		return 10;
544 	}
545 #endif
546 
547 	signal(SIGINT, sighandler);
548 #if !defined(WIN32) && !defined(_WIN32)
549 	/* because windows is too dumb to have SIGUSR1... */
550 	/* dynamic vhost create / destroy toggle (on port + 1) */
551 	signal(SIGUSR1, sighandler);
552 #endif
553 
554 	/* tell the library what debug level to emit and to send it to stderr */
555 	lws_set_log_level(debug_level, NULL);
556 
557 	lwsl_notice("libwebsockets test server - license MIT\n");
558 	lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
559 
560 	printf("Using resource path \"%s\"\n", resource_path);
561 #if defined(LWS_WITH_EXTERNAL_POLL)
562 #if !defined(WIN32) && !defined(_WIN32) && !defined(__ANDROID__)
563 	max_poll_elements = getdtablesize();
564 #else
565 	max_poll_elements = sysconf(_SC_OPEN_MAX);
566 #endif
567 	pollfds = malloc((unsigned int)max_poll_elements * sizeof (struct lws_pollfd));
568 	fd_lookup = malloc((unsigned int)max_poll_elements * sizeof (int));
569 	if (pollfds == NULL || fd_lookup == NULL) {
570 		lwsl_err("Out of memory pollfds=%d\n", max_poll_elements);
571 		return -1;
572 	}
573 	for (n = 0; n < max_poll_elements; n++)
574 		pollfds[n].fd = LWS_SOCK_INVALID;
575 	count_pollfds = 0;
576 #endif
577 
578 	info.iface = iface;
579 	info.protocols = protocols;
580 
581 #if defined(LWS_WITH_TLS)
582 	info.ssl_cert_filepath = NULL;
583 	info.ssl_private_key_filepath = NULL;
584 
585 	if (use_ssl) {
586 		if (strlen(resource_path) > sizeof(cert_path) - 32) {
587 			lwsl_err("resource path too long\n");
588 			return -1;
589 		}
590 		if (!cert_path[0])
591 			sprintf(cert_path, "%s/libwebsockets-test-server.pem",
592 								resource_path);
593 		if (strlen(resource_path) > sizeof(key_path) - 32) {
594 			lwsl_err("resource path too long\n");
595 			return -1;
596 		}
597 		if (!key_path[0])
598 			sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
599 								resource_path);
600 #if defined(LWS_WITH_TLS)
601 		info.ssl_cert_filepath = cert_path;
602 		info.ssl_private_key_filepath = key_path;
603 		if (ca_path[0])
604 			info.ssl_ca_filepath = ca_path;
605 #endif
606 	}
607 #endif
608 	info.gid = gid;
609 	info.uid = uid;
610 	info.options = opts | LWS_SERVER_OPTION_VALIDATE_UTF8 | LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
611 #if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
612 	info.extensions = exts;
613 #endif
614 	info.timeout_secs = 5;
615 #if defined(LWS_WITH_TLS)
616 	info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
617 			       "ECDHE-RSA-AES256-GCM-SHA384:"
618 			       "DHE-RSA-AES256-GCM-SHA384:"
619 			       "ECDHE-RSA-AES256-SHA384:"
620 			       "HIGH:!aNULL:!eNULL:!EXPORT:"
621 			       "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
622 			       "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
623 			       "!DHE-RSA-AES128-SHA256:"
624 			       "!AES128-GCM-SHA256:"
625 			       "!AES128-SHA256:"
626 			       "!DHE-RSA-AES256-SHA256:"
627 			       "!AES256-GCM-SHA384:"
628 			       "!AES256-SHA256";
629 #endif
630 	info.mounts = &mount;
631 #if defined(LWS_WITH_PEER_LIMITS)
632 	info.ip_limit_ah = 128; /* for testing */
633 	info.ip_limit_wsi = 800; /* for testing */
634 #endif
635 
636 	if (use_ssl)
637 		/* redirect guys coming on http */
638 		info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS;
639 
640 	context = lws_create_context(&info);
641 	if (context == NULL) {
642 		lwsl_err("libwebsocket init failed\n");
643 		return -1;
644 	}
645 
646 	info.pvo = &pvo;
647 
648 	vhost = lws_create_vhost(context, &info);
649 	if (!vhost) {
650 		lwsl_err("vhost creation failed\n");
651 		return -1;
652 	}
653 
654 	/*
655 	 * For testing dynamic vhost create / destroy later, we use port + 1
656 	 * Normally if you were creating more vhosts, you would set info.name
657 	 * for each to be the hostname external clients use to reach it
658 	 */
659 
660 	info.port++;
661 
662 #if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_TLS)
663 	lws_init_vhost_client_ssl(&info, vhost);
664 #endif
665 
666 	/* this shows how to override the lws file operations.	You don't need
667 	 * to do any of this unless you have a reason (eg, want to serve
668 	 * compressed files without decompressing the whole archive)
669 	 */
670 	/* stash original platform fops */
671 	fops_plat = *(lws_get_fops(context));
672 	/* override the active fops */
673 	lws_get_fops(context)->open = test_server_fops_open;
674 
675 	n = 0;
676 	while (n >= 0 && !force_exit) {
677 		struct timeval tv;
678 
679 		gettimeofday(&tv, NULL);
680 
681 		/*
682 		 * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
683 		 * live websocket connection using the DUMB_INCREMENT protocol,
684 		 * as soon as it can take more packets (usually immediately)
685 		 */
686 
687 #if defined(LWS_WITH_EXTERNAL_POLL)
688 		/*
689 		 * this represents an existing server's single poll action
690 		 * which also includes libwebsocket sockets
691 		 */
692 
693 		/* if needed, force-service wsis that may not have read all input */
694 		n = lws_service_adjust_timeout(context, 5000, 0);
695 
696 		n = poll(pollfds, (nfds_t)count_pollfds, n);
697 		if (n < 0)
698 			continue;
699 
700 		if (n) {
701 			for (n = 0; n < count_pollfds; n++)
702 				if (pollfds[n].revents)
703 					/*
704 					* returns immediately if the fd does not
705 					* match anything under libwebsockets
706 					* control
707 					*/
708 					if (lws_service_fd(context,
709 							   &pollfds[n]) < 0)
710 						goto done;
711 		}
712 
713 		lws_service_tsi(context, -1, 0);
714 #else
715 		/*
716 		 * If libwebsockets sockets are all we care about,
717 		 * you can use this api which takes care of the poll()
718 		 * and looping through finding who needed service.
719 		 */
720 
721 		n = lws_service(context, 0);
722 #endif
723 
724 		if (dynamic_vhost_enable && !dynamic_vhost) {
725 			lwsl_notice("creating dynamic vhost...\n");
726 			dynamic_vhost = lws_create_vhost(context, &info);
727 		} else
728 			if (!dynamic_vhost_enable && dynamic_vhost) {
729 				lwsl_notice("destroying dynamic vhost...\n");
730 				lws_vhost_destroy(dynamic_vhost);
731 				dynamic_vhost = NULL;
732 			}
733 
734 	}
735 
736 #if defined(LWS_WITH_EXTERNAL_POLL)
737 done:
738 #endif
739 
740 	lws_context_destroy(context);
741 
742 	lwsl_notice("libwebsockets-test-server exited cleanly\n");
743 
744 	return 0;
745 }
746