1/*
2 * libwebsockets-test-client - 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 "lws_config.h"
22
23#include <stdio.h>
24#include <stdlib.h>
25#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
26#include <getopt.h>
27#endif
28#include <string.h>
29#include <signal.h>
30
31#ifdef _WIN32
32#define random rand
33#include "gettimeofday.h"
34#else
35#include <syslog.h>
36#include <sys/time.h>
37#include <unistd.h>
38#endif
39
40#include <libwebsockets.h>
41
42struct lws_poly_gen {
43	uint32_t cyc[2];
44};
45
46#define block_size (3 * 4096)
47
48static int deny_deflate, longlived, mirror_lifetime, test_post, once;
49static struct lws *wsi_dumb, *wsi_mirror;
50static struct lws *wsi_multi[3];
51static volatile int force_exit;
52static unsigned int opts, rl_multi[3];
53static int flag_no_mirror_traffic, justmirror, flag_echo;
54static uint32_t count_blocks = 1024, txb, rxb, rx_count, errs;
55static struct lws_poly_gen tx = { { 0xabcde, 0x23456789 } },
56			   rx = { { 0xabcde, 0x23456789 } }
57;
58
59#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
60char crl_path[1024] = "";
61#endif
62
63/*
64 * This demo shows how to connect multiple websockets simultaneously to a
65 * websocket server (there is no restriction on their having to be the same
66 * server just it simplifies the demo).
67 *
68 *  dumb-increment-protocol:  we connect to the server and print the number
69 *				we are given
70 *
71 *  lws-mirror-protocol: draws random circles, which are mirrored on to every
72 *				client (see them being drawn in every browser
73 *				session also using the test server)
74 */
75
76enum demo_protocols {
77
78	PROTOCOL_DUMB_INCREMENT,
79	PROTOCOL_LWS_MIRROR,
80
81	/* always last */
82	DEMO_PROTOCOL_COUNT
83};
84
85static uint8_t
86lws_poly_rand(struct lws_poly_gen *p)
87{
88	p->cyc[0] = (p->cyc[0] & 1) ? (p->cyc[0] >> 1) ^ 0xb4bcd35c :
89				      p->cyc[0] >> 1;
90	p->cyc[0] = (p->cyc[0] & 1) ? (p->cyc[0] >> 1) ^ 0xb4bcd35c :
91				      p->cyc[0] >> 1;
92	p->cyc[1] = (p->cyc[1] & 1) ? (p->cyc[1] >> 1) ^ 0x7a5bc2e3 :
93				      p->cyc[1] >> 1;
94
95	return (uint8_t)(p->cyc[0] ^ p->cyc[1]);
96}
97
98static void show_http_content(const char *p, size_t l)
99{
100	if (lwsl_visible(LLL_INFO)) {
101		while (l--)
102			if (*p < 0x7f)
103				putchar(*p++);
104			else
105				putchar('.');
106	}
107}
108
109
110/*
111 * dumb_increment protocol
112 *
113 * since this also happens to be protocols[0], some callbacks that are not
114 * bound to a specific protocol also turn up here.
115 */
116
117static int
118callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
119			void *user, void *in, size_t len)
120{
121#if defined(LWS_WITH_TLS)
122	union lws_tls_cert_info_results ci;
123#if defined(LWS_HAVE_CTIME_R) && !defined(LWS_WITH_NO_LOGS)
124	char date[32];
125#endif
126#endif
127	const char *which = "http";
128       char which_wsi[50], buf[80 + LWS_PRE];
129	int n;
130
131	switch (reason) {
132
133	case LWS_CALLBACK_CLIENT_ESTABLISHED:
134		lwsl_info("dumb: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
135		break;
136
137	case LWS_CALLBACK_CLOSED:
138		lwsl_notice("dumb: LWS_CALLBACK_CLOSED\n");
139		wsi_dumb = NULL;
140		break;
141
142	case LWS_CALLBACK_CLIENT_RECEIVE:
143		((char *)in)[len] = '\0';
144		lwsl_info("rx %d '%s'\n", (int)len, (char *)in);
145		break;
146
147	/* because we are protocols[0] ... */
148
149	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
150		if (wsi == wsi_dumb) {
151			which = "dumb";
152			wsi_dumb = NULL;
153		}
154		if (wsi == wsi_mirror) {
155			which = "mirror";
156			wsi_mirror = NULL;
157		}
158
159		for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++)
160			if (wsi == wsi_multi[n]) {
161				sprintf(which_wsi, "multi %d", n);
162				which = which_wsi;
163				wsi_multi[n] = NULL;
164			}
165
166		lwsl_err("CLIENT_CONNECTION_ERROR: %s: %s\n", which,
167			 in ? (char *)in : "(null)");
168		break;
169
170	case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
171		if ((strcmp((const char *)in, "deflate-stream") == 0) &&
172		    deny_deflate) {
173			lwsl_notice("denied deflate-stream extension\n");
174			return 1;
175		}
176		if ((strcmp((const char *)in, "x-webkit-deflate-frame") == 0))
177			return 1;
178		if ((strcmp((const char *)in, "deflate-frame") == 0))
179			return 1;
180		break;
181
182	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
183		lwsl_notice("lws_http_client_http_response %d\n",
184				lws_http_client_http_response(wsi));
185#if defined(LWS_WITH_TLS)
186		if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME,
187					    &ci, sizeof(ci.ns.name)))
188			lwsl_notice(" Peer Cert CN        : %s\n", ci.ns.name);
189
190		if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_ISSUER_NAME,
191					    &ci, sizeof(ci.ns.name)))
192			lwsl_notice(" Peer Cert issuer    : %s\n", ci.ns.name);
193
194		if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_FROM,
195					    &ci, 0))
196#if defined(LWS_HAVE_CTIME_R)
197			lwsl_notice(" Peer Cert Valid from: %s",
198						ctime_r(&ci.time, date));
199#else
200			lwsl_notice(" Peer Cert Valid from: %s",
201						ctime(&ci.time));
202#endif
203		if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_TO,
204					    &ci, 0))
205#if defined(LWS_HAVE_CTIME_R)
206			lwsl_notice(" Peer Cert Valid to  : %s",
207						ctime_r(&ci.time, date));
208#else
209			lwsl_notice(" Peer Cert Valid to  : %s",
210						ctime(&ci.time));
211#endif
212		if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_USAGE,
213					    &ci, 0))
214			lwsl_notice(" Peer Cert usage bits: 0x%x\n", ci.usage);
215#endif
216		break;
217
218	/* chunked content */
219	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
220		lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: %ld\n",
221			    (long)len);
222		show_http_content(in, len);
223		break;
224
225	/* unchunked content */
226	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
227		{
228			char buffer[1024 + LWS_PRE];
229			char *px = buffer + LWS_PRE;
230			int lenx = sizeof(buffer) - LWS_PRE;
231
232			/*
233			 * Often you need to flow control this by something
234			 * else being writable.  In that case call the api
235			 * to get a callback when writable here, and do the
236			 * pending client read in the writeable callback of
237			 * the output.
238			 *
239			 * In the case of chunked content, this will call back
240			 * LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ once per
241			 * chunk or partial chunk in the buffer, and report
242			 * zero length back here.
243			 */
244			if (lws_http_client_read(wsi, &px, &lenx) < 0)
245				return -1;
246		}
247		break;
248
249	case LWS_CALLBACK_CLIENT_WRITEABLE:
250		lwsl_info("Client wsi %p writable\n", wsi);
251		break;
252
253	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
254		if (test_post) {
255			unsigned char **p = (unsigned char **)in, *end = (*p) + len;
256
257			if (lws_add_http_header_by_token(wsi,
258					WSI_TOKEN_HTTP_CONTENT_LENGTH,
259					(unsigned char *)"29", 2, p, end))
260				return -1;
261			if (lws_add_http_header_by_token(wsi,
262					WSI_TOKEN_HTTP_CONTENT_TYPE,
263					(unsigned char *)"application/x-www-form-urlencoded",
264					33, p, end))
265				return -1;
266
267			/* inform lws we have http body to send */
268			lws_client_http_body_pending(wsi, 1);
269			lws_callback_on_writable(wsi);
270		}
271		break;
272
273	case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
274		strcpy(buf + LWS_PRE, "text=hello&send=Send+the+form");
275		n = lws_write(wsi, (unsigned char *)&buf[LWS_PRE],
276			      strlen(&buf[LWS_PRE]), LWS_WRITE_HTTP);
277		if (n < 0)
278			return -1;
279		/* we only had one thing to send, so inform lws we are done
280		 * if we had more to send, call lws_callback_on_writable(wsi);
281		 * and just return 0 from callback.  On having sent the last
282		 * part, call the below api instead.*/
283		lws_client_http_body_pending(wsi, 0);
284		break;
285
286	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
287		wsi_dumb = NULL;
288		force_exit = 1;
289		break;
290
291#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param) && \
292	!defined(LWS_WITH_MBEDTLS)
293	case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
294		if (crl_path[0]) {
295			/* Enable CRL checking of the server certificate */
296			X509_STORE *store;
297			X509_LOOKUP *lookup;
298			int n;
299			X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
300			X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
301			SSL_CTX_set1_param((SSL_CTX*)user, param);
302			store = SSL_CTX_get_cert_store((SSL_CTX*)user);
303			lookup = X509_STORE_add_lookup(store,
304							X509_LOOKUP_file());
305			n = X509_load_cert_crl_file(lookup, crl_path,
306							X509_FILETYPE_PEM);
307			X509_VERIFY_PARAM_free(param);
308			if (n != 1) {
309				char errbuf[256];
310				const char *es;
311
312				n = (int)ERR_get_error();
313				es = ERR_error_string(
314#if defined(LWS_WITH_BORINGSSL)
315							 (uint32_t)
316#else
317							 (unsigned long)
318#endif
319							 n, errbuf);
320				lwsl_err("EXTRA_CLIENT_VERIFY_CERTS: "
321					 "SSL error: %s (%d)\n", es, n);
322				return 1;
323			}
324		}
325		break;
326#endif
327
328	default:
329		break;
330	}
331
332	return 0;
333}
334
335
336/* lws-mirror_protocol */
337
338
339static int
340callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
341		    void *user, void *in, size_t len)
342{
343	unsigned char buf[LWS_PRE + block_size], *p;
344	unsigned int rands[4];
345	int l = 0;
346	int n;
347
348	switch (reason) {
349	case LWS_CALLBACK_CLIENT_ESTABLISHED:
350
351		lwsl_notice("mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
352
353		if (flag_echo) {
354			rxb = txb = 0;
355			rx.cyc[0] = tx.cyc[0] = 0xabcde;
356			rx.cyc[1] = tx.cyc[1] = 0x23456789;
357
358			lws_callback_on_writable(wsi);
359
360			break;
361		}
362
363		lws_get_random(lws_get_context(wsi), rands, sizeof(rands[0]));
364		mirror_lifetime = (int)(16384 + (rands[0] & 65535));
365		/* useful to test single connection stability */
366		if (longlived)
367			mirror_lifetime += 500000;
368
369		lwsl_notice("opened mirror connection with "
370			  "%d lifetime\n", mirror_lifetime);
371
372		/*
373		 * mirror_lifetime is decremented each send, when it reaches
374		 * zero the connection is closed in the send callback.
375		 * When the close callback comes, wsi_mirror is set to NULL
376		 * so a new connection will be opened
377		 *
378		 * start the ball rolling,
379		 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
380		 */
381		if (!flag_no_mirror_traffic)
382			lws_callback_on_writable(wsi);
383		break;
384
385	case LWS_CALLBACK_CLIENT_CLOSED:
386		lwsl_notice("mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d, "
387			    "rxb %d, rx_count %d\n", mirror_lifetime, rxb,
388			    rx_count);
389		wsi_mirror = NULL;
390		if (flag_echo || once)
391			force_exit = 1;
392		break;
393
394	case LWS_CALLBACK_CLIENT_WRITEABLE:
395		lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n");
396		if (flag_no_mirror_traffic)
397			return 0;
398
399		if (flag_echo) {
400			for (n = 0; n < (int)block_size; n++)
401				buf[LWS_PRE + n] = lws_poly_rand(&tx);
402
403			n = lws_write(wsi, &buf[LWS_PRE], block_size,
404				      opts | LWS_WRITE_TEXT);
405			if (n < 0) {
406				lwsl_err("Error sending\n");
407				return -1;
408			}
409
410			txb++;
411			if (txb != count_blocks)
412				lws_callback_on_writable(wsi);
413			else {
414				lwsl_notice("send completed: %d x %d\n",
415					    count_blocks, block_size);
416			}
417			break;
418		}
419
420		for (n = 0; n < 1; n++) {
421			lws_get_random(lws_get_context(wsi), rands,
422				       sizeof(rands));
423			l += sprintf((char *)&buf[LWS_PRE + l],
424					"c #%06X %u %u %u;",
425					rands[0] & 0xffffff,	/* colour */
426					rands[1] & 511,		/* x */
427					rands[2] & 255,		/* y */
428					(rands[3] & 31) + 1);	/* radius */
429		}
430
431		n = (int)lws_write(wsi, &buf[LWS_PRE], (unsigned int)l,
432			      opts | LWS_WRITE_TEXT);
433		if (n < 0)
434			return -1;
435		if (n < l) {
436			lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
437			return -1;
438		}
439		if (!justmirror)
440			mirror_lifetime--;
441		if (!mirror_lifetime) {
442			lwsl_notice("closing mirror session\n");
443			return -1;
444		}
445		/* get notified as soon as we can write again */
446		lws_callback_on_writable(wsi);
447
448#if !defined(_WIN32) && !defined(WIN32)
449		usleep(50);
450#endif
451		break;
452
453	case LWS_CALLBACK_CLIENT_RECEIVE:
454		if (flag_echo) {
455			p = (unsigned char *)in;
456			for (n = 0; n < (int)len; n++)
457				if (*p++ != lws_poly_rand(&rx)) {
458					lwsl_err("mismatch at rxb %d offset %d\n", (int)rxb + (n / block_size), n % block_size);
459					errs++;
460					force_exit = 1;
461					return -1;
462				}
463			rx_count += (unsigned int)(unsigned long long)len;
464			while (rx_count >= block_size) {
465				rx_count -= block_size;
466				rxb++;
467			}
468			if (rx_count == 0 && rxb == count_blocks) {
469				lwsl_notice("Everything received: errs %d\n",
470					    errs);
471				force_exit = 1;
472				return -1;
473			}
474		}
475		break;
476	default:
477		break;
478	}
479
480	return 0;
481}
482
483static int
484callback_test_raw_client(struct lws *wsi, enum lws_callback_reasons reason,
485			 void *user, void *in, size_t len)
486{
487	switch (reason) {
488	case LWS_CALLBACK_RAW_ADOPT:
489		lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
490		break;
491
492	case LWS_CALLBACK_RAW_RX:
493		lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
494		puts(in);
495		break;
496
497	case LWS_CALLBACK_RAW_CLOSE:
498		lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
499		break;
500
501	case LWS_CALLBACK_RAW_WRITEABLE:
502		lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
503		break;
504
505	default:
506		break;
507	}
508
509	return 0;
510}
511
512/* list of supported protocols and callbacks */
513
514static const struct lws_protocols protocols[] = {
515	{
516		"dumb-increment-protocol",
517		callback_dumb_increment,
518		0, 20, 0, NULL, 0
519	},
520	{
521		"lws-mirror-protocol",
522		callback_lws_mirror,
523		0, 4096, 0, NULL, 0
524	}, {
525		"lws-test-raw-client",
526		callback_test_raw_client,
527		0, 128, 0, NULL, 0
528	},
529	LWS_PROTOCOL_LIST_TERM
530};
531
532#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
533static const struct lws_extension exts[] = {
534	{
535		"permessage-deflate",
536		lws_extension_callback_pm_deflate,
537		"permessage-deflate; client_no_context_takeover"
538	},
539	{
540		"deflate-frame",
541		lws_extension_callback_pm_deflate,
542		"deflate_frame"
543	},
544	{ NULL, NULL, NULL /* terminator */ }
545};
546#endif
547
548
549void sighandler(int sig)
550{
551	force_exit = 1;
552}
553
554#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
555static struct option options[] = {
556	{ "help",	no_argument,		NULL, 'h' },
557	{ "debug",      required_argument,      NULL, 'd' },
558	{ "port",	required_argument,	NULL, 'p' },
559	{ "ssl",	no_argument,		NULL, 's' },
560	{ "strict-ssl",	no_argument,		NULL, 'S' },
561	{ "version",	required_argument,	NULL, 'v' },
562	{ "undeflated",	no_argument,		NULL, 'u' },
563	{ "echo",	no_argument,		NULL, 'e' },
564	{ "multi-test",	no_argument,		NULL, 'm' },
565	{ "nomirror",	no_argument,		NULL, 'n' },
566	{ "justmirror",	no_argument,		NULL, 'j' },
567	{ "longlived",	no_argument,		NULL, 'l' },
568	{ "post",	no_argument,		NULL, 'o' },
569	{ "once",	no_argument,		NULL, 'O' },
570	{ "ssl-cert",  required_argument,	NULL, 'C' },
571	{ "ssl-key",  required_argument,	NULL, 'K' },
572	{ "ssl-ca",  required_argument,		NULL, 'A' },
573#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
574	{ "ssl-crl",  required_argument,		NULL, 'R' },
575#endif
576	{ NULL, 0, 0, 0 }
577};
578#endif
579
580static int ratelimit_connects(unsigned int *last, unsigned int secs)
581{
582	struct timeval tv;
583
584	gettimeofday(&tv, NULL);
585
586	if ((unsigned long)tv.tv_sec - (unsigned long)(*last) < (unsigned long)secs)
587		return 0;
588
589	*last = (unsigned int)tv.tv_sec;
590
591	return 1;
592}
593
594int main(int argc, char **argv)
595{
596	int n = 0, m, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
597	unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1, do_multi = 0;
598	struct lws_context_creation_info info;
599	struct lws_client_connect_info i;
600	struct lws_context *context;
601	const char *prot, *p;
602	char path[300];
603	char cert_path[1024] = "";
604	char key_path[1024] = "";
605	char ca_path[1024] = "";
606	unsigned long last = lws_now_secs();
607
608	memset(&info, 0, sizeof info);
609
610	lwsl_notice("libwebsockets test client - license MIT\n");
611	lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
612
613	if (argc < 2)
614		goto usage;
615
616	while (n >= 0) {
617#if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
618       n = getopt_long(argc, argv, "Sjnuv:hsp:d:lC:K:A:moeO", options, NULL);
619#else
620       n = getopt(argc, argv, "Sjnuv:hsp:d:lC:K:A:moeO");
621#endif
622		if (n < 0)
623			continue;
624		switch (n) {
625		case 'd':
626			lws_set_log_level(atoi(optarg), NULL);
627			break;
628		case 's': /* lax SSL, allow selfsigned, skip checking hostname */
629			use_ssl = LCCSCF_USE_SSL |
630				  LCCSCF_ALLOW_SELFSIGNED |
631				  LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
632			break;
633		case 'S': /* Strict SSL, no selfsigned, check server hostname */
634			use_ssl = LCCSCF_USE_SSL;
635			break;
636		case 'p':
637			port = atoi(optarg);
638			break;
639		case 'e':
640			flag_echo = 1;
641			break;
642		case 'j':
643			justmirror = 1;
644			break;
645		case 'l':
646			longlived = 1;
647			break;
648		case 'v':
649			ietf_version = atoi(optarg);
650			break;
651		case 'u':
652			deny_deflate = 1;
653			break;
654		case 'm':
655			do_multi = 1;
656			break;
657		case 'o':
658			test_post = 1;
659			break;
660		case 'O':
661			once = 1;
662			break;
663		case 'n':
664			flag_no_mirror_traffic = 1;
665			lwsl_notice("Disabled sending mirror data (for pingpong testing)\n");
666			break;
667		case 'C':
668			lws_strncpy(cert_path, optarg, sizeof(cert_path));
669			break;
670		case 'K':
671			lws_strncpy(key_path, optarg, sizeof(key_path));
672			break;
673		case 'A':
674			lws_strncpy(ca_path, optarg, sizeof(ca_path));
675			break;
676
677#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
678		case 'R':
679			lws_strncpy(crl_path, optarg, sizeof(crl_path));
680			break;
681#endif
682		case 'h':
683			goto usage;
684		}
685	}
686
687	if (optind >= argc)
688		goto usage;
689
690	signal(SIGINT, sighandler);
691
692	memset(&i, 0, sizeof(i));
693
694	i.port = port;
695	if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
696		goto usage;
697
698	/* add back the leading / on path */
699	if (p[0] != '/') {
700		path[0] = '/';
701		lws_strncpy(path + 1, p, sizeof(path) - 1);
702		i.path = path;
703	} else
704		i.path = p;
705
706	if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
707		use_ssl = 0;
708	if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
709		if (!use_ssl)
710			use_ssl = LCCSCF_USE_SSL;
711
712	lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path);
713
714	/*
715	 * create the websockets context.  This tracks open connections and
716	 * knows how to route any traffic and which protocol version to use,
717	 * and if each connection is client or server side.
718	 *
719	 * For this client-only demo, we tell it to not listen on any port.
720	 */
721
722	info.port = CONTEXT_PORT_NO_LISTEN;
723	info.protocols = protocols;
724	info.gid = (gid_t)-1;
725	info.uid = (uid_t)-1;
726#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
727	info.extensions = exts;
728#endif
729
730	/*
731	 * since we know this lws context is only ever going to be used with
732	 * a few client wsis / fds / sockets at a time, let lws know it doesn't
733	 * have to use the default allocations for fd tables up to ulimit -n.
734	 * It will just allocate for 2 internal and 4 that we might use.
735	 */
736	info.fd_limit_per_thread = 2 + 4;
737
738#if defined(LWS_WITH_TLS)
739	info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
740#endif
741
742	info.options |= LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
743#if defined(LWS_WITH_TLS)
744	if (use_ssl) {
745		/*
746		 * If the server wants us to present a valid SSL client certificate
747		 * then we can set it up here.
748		 */
749
750		if (cert_path[0])
751			info.client_ssl_cert_filepath = cert_path;
752		if (key_path[0])
753			info.client_ssl_private_key_filepath = key_path;
754
755		/*
756		 * A CA cert and CRL can be used to validate the cert send by the server
757		 */
758		if (ca_path[0])
759			info.client_ssl_ca_filepath = ca_path;
760
761#if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
762		else if (crl_path[0])
763			lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
764#endif
765	}
766
767	if (use_ssl & LCCSCF_USE_SSL) {
768		lwsl_notice(" Using SSL\n");
769#if defined(LWS_WITH_MBEDTLS)
770		lwsl_notice("   (NOTE: mbedtls needs to be given the remote\n");
771		lwsl_notice("    CA cert to trust (with -A) to validate it)\n");
772#endif
773	}
774	else
775		lwsl_notice(" SSL disabled\n");
776	if (use_ssl & LCCSCF_ALLOW_SELFSIGNED)
777		lwsl_notice(" Selfsigned certs allowed\n");
778	else
779		lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
780	if (use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)
781		lwsl_notice(" Skipping peer cert hostname check\n");
782	else
783		lwsl_notice(" Requiring peer cert hostname matches\n");
784#endif
785	context = lws_create_context(&info);
786	if (context == NULL) {
787		fprintf(stderr, "Creating libwebsocket context failed\n");
788		return 1;
789	}
790
791	i.context = context;
792	i.ssl_connection = use_ssl;
793	i.host = i.address;
794	i.origin = i.address;
795	i.ietf_version_or_minus_one = ietf_version;
796
797	if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
798		lwsl_notice("using %s mode (non-ws)\n", prot);
799		if (test_post) {
800			i.method = "POST";
801			lwsl_notice("POST mode\n");
802		}
803		else
804			i.method = "GET";
805		do_ws = 0;
806	} else
807		if (!strcmp(prot, "raw")) {
808			i.method = "RAW";
809			i.protocol = "lws-test-raw-client";
810			lwsl_notice("using RAW mode connection\n");
811			do_ws = 0;
812		} else
813			lwsl_notice("using %s mode (ws)\n", prot);
814
815	/*
816	 * sit there servicing the websocket context to handle incoming
817	 * packets, and drawing random circles on the mirror protocol websocket
818	 *
819	 * nothing happens until the client websocket connection is
820	 * asynchronously established... calling lws_client_connect() only
821	 * instantiates the connection logically, lws_service() progresses it
822	 * asynchronously.
823	 */
824
825	m = 0;
826	while (!force_exit) {
827
828		if (do_multi) {
829			for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++) {
830				if (!wsi_multi[n] && ratelimit_connects(&rl_multi[n], 2u)) {
831					lwsl_notice("dumb %d: connecting\n", n);
832					i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
833					i.pwsi = &wsi_multi[n];
834					lws_client_connect_via_info(&i);
835				}
836			}
837		} else {
838
839			if (do_ws) {
840				if (!flag_echo && !justmirror && !wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
841					lwsl_notice("dumb: connecting\n");
842					i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
843					i.pwsi = &wsi_dumb;
844					lws_client_connect_via_info(&i);
845				}
846
847				if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
848					lwsl_notice("mirror: connecting\n");
849					i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
850					i.pwsi = &wsi_mirror;
851					wsi_mirror = lws_client_connect_via_info(&i);
852				}
853			} else
854				if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
855					lwsl_notice("http: connecting\n");
856					i.pwsi = &wsi_dumb;
857					lws_client_connect_via_info(&i);
858				}
859		}
860
861		lws_service(context, 500);
862
863		if (do_multi) {
864			m++;
865			if (m == 10) {
866				m = 0;
867				lwsl_notice("doing lws_callback_on_writable_all_protocol\n");
868				lws_callback_on_writable_all_protocol(context,
869					   &protocols[PROTOCOL_DUMB_INCREMENT]);
870			}
871		}
872
873		if (flag_echo && lws_now_secs() != last) {
874			lwsl_notice("rxb %d, rx_count %d\n", rxb, rx_count);
875			last = lws_now_secs();
876		}
877	}
878
879	lwsl_err("Exiting\n");
880	lws_context_destroy(context);
881
882	return ret;
883
884usage:
885	fprintf(stderr, "Usage: libwebsockets-test-client "
886				"<server address> [--port=<p>] "
887				"[--ssl] [-k] [-v <ver>] "
888				"[-d <log bitfield>] [-l]\n");
889	return 1;
890}
891