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