Imported Upstream version 3.2.0
[platform/upstream/libwebsockets.git] / test-apps / test-client.c
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
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
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
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_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
283                         X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
284                         SSL_CTX_set1_param((SSL_CTX*)user, param);
285                         X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX*)user);
286                         X509_LOOKUP *lookup = X509_STORE_add_lookup(store,
287                                                         X509_LOOKUP_file());
288                         int n = X509_load_cert_crl_file(lookup, crl_path,
289                                                         X509_FILETYPE_PEM);
290                         X509_VERIFY_PARAM_free(param);
291                         if (n != 1) {
292                                 char errbuf[256];
293                                 n = ERR_get_error();
294                                 lwsl_err("EXTRA_CLIENT_VERIFY_CERTS: "
295                                          "SSL error: %s (%d)\n",
296                                          ERR_error_string(n, errbuf), n);
297                                 return 1;
298                         }
299                 }
300                 break;
301 #endif
302
303         default:
304                 break;
305         }
306
307         return 0;
308 }
309
310
311 /* lws-mirror_protocol */
312
313
314 static int
315 callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
316                     void *user, void *in, size_t len)
317 {
318         unsigned char buf[LWS_PRE + block_size], *p;
319         unsigned int rands[4];
320         int l = 0;
321         int n;
322
323         switch (reason) {
324         case LWS_CALLBACK_CLIENT_ESTABLISHED:
325
326                 lwsl_notice("mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n");
327
328                 if (flag_echo) {
329                         rxb = txb = 0;
330                         rx.cyc[0] = tx.cyc[0] = 0xabcde;
331                         rx.cyc[1] = tx.cyc[1] = 0x23456789;
332
333                         lws_callback_on_writable(wsi);
334
335                         break;
336                 }
337
338                 lws_get_random(lws_get_context(wsi), rands, sizeof(rands[0]));
339                 mirror_lifetime = 16384 + (rands[0] & 65535);
340                 /* useful to test single connection stability */
341                 if (longlived)
342                         mirror_lifetime += 500000;
343
344                 lwsl_notice("opened mirror connection with "
345                           "%d lifetime\n", mirror_lifetime);
346
347                 /*
348                  * mirror_lifetime is decremented each send, when it reaches
349                  * zero the connection is closed in the send callback.
350                  * When the close callback comes, wsi_mirror is set to NULL
351                  * so a new connection will be opened
352                  *
353                  * start the ball rolling,
354                  * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
355                  */
356                 if (!flag_no_mirror_traffic)
357                         lws_callback_on_writable(wsi);
358                 break;
359
360         case LWS_CALLBACK_CLIENT_CLOSED:
361                 lwsl_notice("mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d, "
362                             "rxb %d, rx_count %d\n", mirror_lifetime, rxb,
363                             rx_count);
364                 wsi_mirror = NULL;
365                 if (flag_echo || once)
366                         force_exit = 1;
367                 break;
368
369         case LWS_CALLBACK_CLIENT_WRITEABLE:
370                 lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n");
371                 if (flag_no_mirror_traffic)
372                         return 0;
373
374                 if (flag_echo) {
375                         for (n = 0; n < (int)block_size; n++)
376                                 buf[LWS_PRE + n] = lws_poly_rand(&tx);
377
378                         n = lws_write(wsi, &buf[LWS_PRE], block_size,
379                                       opts | LWS_WRITE_TEXT);
380                         if (n < 0) {
381                                 lwsl_err("Error sending\n");
382                                 return -1;
383                         }
384
385                         txb++;
386                         if (txb != count_blocks)
387                                 lws_callback_on_writable(wsi);
388                         else {
389                                 lwsl_notice("send completed: %d x %d\n",
390                                             count_blocks, block_size);
391                         }
392                         break;
393                 }
394
395                 for (n = 0; n < 1; n++) {
396                         lws_get_random(lws_get_context(wsi), rands,
397                                        sizeof(rands));
398                         l += sprintf((char *)&buf[LWS_PRE + l],
399                                         "c #%06X %u %u %u;",
400                                         rands[0] & 0xffffff,    /* colour */
401                                         rands[1] & 511,         /* x */
402                                         rands[2] & 255,         /* y */
403                                         (rands[3] & 31) + 1);   /* radius */
404                 }
405
406                 n = lws_write(wsi, &buf[LWS_PRE], l,
407                               opts | LWS_WRITE_TEXT);
408                 if (n < 0)
409                         return -1;
410                 if (n < l) {
411                         lwsl_err("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n");
412                         return -1;
413                 }
414                 if (!justmirror)
415                         mirror_lifetime--;
416                 if (!mirror_lifetime) {
417                         lwsl_notice("closing mirror session\n");
418                         return -1;
419                 }
420                 /* get notified as soon as we can write again */
421                 lws_callback_on_writable(wsi);
422
423 #if !defined(_WIN32) && !defined(WIN32)
424                 usleep(50);
425 #endif
426                 break;
427
428         case LWS_CALLBACK_CLIENT_RECEIVE:
429                 if (flag_echo) {
430                         p = (unsigned char *)in;
431                         for (n = 0; n < (int)len; n++)
432                                 if (*p++ != lws_poly_rand(&rx)) {
433                                         lwsl_err("mismatch at rxb %d offset %d\n", rxb + (n / block_size), n % block_size);
434                                         errs++;
435                                         force_exit = 1;
436                                         return -1;
437                                 }
438                         rx_count += (unsigned int)(unsigned long long)len;
439                         while (rx_count >= block_size) {
440                                 rx_count -= block_size;
441                                 rxb++;
442                         }
443                         if (rx_count == 0 && rxb == count_blocks) {
444                                 lwsl_notice("Everything received: errs %d\n",
445                                             errs);
446                                 force_exit = 1;
447                                 return -1;
448                         }
449                 }
450                 break;
451         default:
452                 break;
453         }
454
455         return 0;
456 }
457
458 static int
459 callback_test_raw_client(struct lws *wsi, enum lws_callback_reasons reason,
460                          void *user, void *in, size_t len)
461 {
462         switch (reason) {
463         case LWS_CALLBACK_RAW_ADOPT:
464                 lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n");
465                 break;
466
467         case LWS_CALLBACK_RAW_RX:
468                 lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len);
469                 puts(in);
470                 break;
471
472         case LWS_CALLBACK_RAW_CLOSE:
473                 lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n");
474                 break;
475
476         case LWS_CALLBACK_RAW_WRITEABLE:
477                 lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n");
478                 break;
479
480         default:
481                 break;
482         }
483
484         return 0;
485 }
486
487 /* list of supported protocols and callbacks */
488
489 static const struct lws_protocols protocols[] = {
490         {
491                 "dumb-increment-protocol",
492                 callback_dumb_increment,
493                 0,
494                 20,
495         },
496         {
497                 "lws-mirror-protocol",
498                 callback_lws_mirror,
499                 0,
500                 4096,
501         }, {
502                 "lws-test-raw-client",
503                 callback_test_raw_client,
504                 0,
505                 128
506         },
507         { NULL, NULL, 0, 0 } /* end */
508 };
509
510 static const struct lws_extension exts[] = {
511         {
512                 "permessage-deflate",
513                 lws_extension_callback_pm_deflate,
514                 "permessage-deflate; client_no_context_takeover"
515         },
516         {
517                 "deflate-frame",
518                 lws_extension_callback_pm_deflate,
519                 "deflate_frame"
520         },
521         { NULL, NULL, NULL /* terminator */ }
522 };
523
524
525
526 void sighandler(int sig)
527 {
528         force_exit = 1;
529 }
530
531 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
532 static struct option options[] = {
533         { "help",       no_argument,            NULL, 'h' },
534         { "debug",      required_argument,      NULL, 'd' },
535         { "port",       required_argument,      NULL, 'p' },
536         { "ssl",        no_argument,            NULL, 's' },
537         { "strict-ssl", no_argument,            NULL, 'S' },
538         { "version",    required_argument,      NULL, 'v' },
539         { "undeflated", no_argument,            NULL, 'u' },
540         { "echo",       no_argument,            NULL, 'e' },
541         { "multi-test", no_argument,            NULL, 'm' },
542         { "nomirror",   no_argument,            NULL, 'n' },
543         { "justmirror", no_argument,            NULL, 'j' },
544         { "longlived",  no_argument,            NULL, 'l' },
545         { "post",       no_argument,            NULL, 'o' },
546         { "once",       no_argument,            NULL, 'O' },
547         { "pingpong-secs", required_argument,   NULL, 'P' },
548         { "ssl-cert",  required_argument,       NULL, 'C' },
549         { "ssl-key",  required_argument,        NULL, 'K' },
550         { "ssl-ca",  required_argument,         NULL, 'A' },
551 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
552         { "ssl-crl",  required_argument,                NULL, 'R' },
553 #endif
554         { NULL, 0, 0, 0 }
555 };
556 #endif
557
558 static int ratelimit_connects(unsigned int *last, unsigned int secs)
559 {
560         struct timeval tv;
561
562         gettimeofday(&tv, NULL);
563
564         if (tv.tv_sec - (*last) < secs)
565                 return 0;
566
567         *last = tv.tv_sec;
568
569         return 1;
570 }
571
572 int main(int argc, char **argv)
573 {
574         int n = 0, m, ret = 0, port = 7681, use_ssl = 0, ietf_version = -1;
575         unsigned int rl_dumb = 0, rl_mirror = 0, do_ws = 1, pp_secs = 0,
576                      do_multi = 0;
577         struct lws_context_creation_info info;
578         struct lws_client_connect_info i;
579         struct lws_context *context;
580         const char *prot, *p;
581         char path[300];
582         char cert_path[1024] = "";
583         char key_path[1024] = "";
584         char ca_path[1024] = "";
585         unsigned long last = lws_now_secs();
586
587         memset(&info, 0, sizeof info);
588
589         lwsl_notice("libwebsockets test client - license LGPL2.1+SLE\n");
590         lwsl_notice("(C) Copyright 2010-2018 Andy Green <andy@warmcat.com>\n");
591
592         if (argc < 2)
593                 goto usage;
594
595         while (n >= 0) {
596 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
597        n = getopt_long(argc, argv, "Sjnuv:hsp:d:lC:K:A:P:moeO", options, NULL);
598 #else
599        n = getopt(argc, argv, "Sjnuv:hsp:d:lC:K:A:P:moeO");
600 #endif
601                 if (n < 0)
602                         continue;
603                 switch (n) {
604                 case 'd':
605                         lws_set_log_level(atoi(optarg), NULL);
606                         break;
607                 case 's': /* lax SSL, allow selfsigned, skip checking hostname */
608                         use_ssl = LCCSCF_USE_SSL |
609                                   LCCSCF_ALLOW_SELFSIGNED |
610                                   LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
611                         break;
612                 case 'S': /* Strict SSL, no selfsigned, check server hostname */
613                         use_ssl = LCCSCF_USE_SSL;
614                         break;
615                 case 'p':
616                         port = atoi(optarg);
617                         break;
618                 case 'e':
619                         flag_echo = 1;
620                         break;
621                 case 'P':
622                         pp_secs = atoi(optarg);
623                         lwsl_notice("Setting pingpong interval to %d\n", pp_secs);
624                         break;
625                 case 'j':
626                         justmirror = 1;
627                         break;
628                 case 'l':
629                         longlived = 1;
630                         break;
631                 case 'v':
632                         ietf_version = atoi(optarg);
633                         break;
634                 case 'u':
635                         deny_deflate = 1;
636                         break;
637                 case 'm':
638                         do_multi = 1;
639                         break;
640                 case 'o':
641                         test_post = 1;
642                         break;
643                 case 'O':
644                         once = 1;
645                         break;
646                 case 'n':
647                         flag_no_mirror_traffic = 1;
648                         lwsl_notice("Disabled sending mirror data (for pingpong testing)\n");
649                         break;
650                 case 'C':
651                         lws_strncpy(cert_path, optarg, sizeof(cert_path));
652                         break;
653                 case 'K':
654                         lws_strncpy(key_path, optarg, sizeof(key_path));
655                         break;
656                 case 'A':
657                         lws_strncpy(ca_path, optarg, sizeof(ca_path));
658                         break;
659
660 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
661                 case 'R':
662                         lws_strncpy(crl_path, optarg, sizeof(crl_path));
663                         break;
664 #endif
665                 case 'h':
666                         goto usage;
667                 }
668         }
669
670         if (optind >= argc)
671                 goto usage;
672
673         signal(SIGINT, sighandler);
674
675         memset(&i, 0, sizeof(i));
676
677         i.port = port;
678         if (lws_parse_uri(argv[optind], &prot, &i.address, &i.port, &p))
679                 goto usage;
680
681         /* add back the leading / on path */
682         if (p[0] != '/') {
683                 path[0] = '/';
684                 lws_strncpy(path + 1, p, sizeof(path) - 1);
685                 i.path = path;
686         } else
687                 i.path = p;
688
689         if (!strcmp(prot, "http") || !strcmp(prot, "ws"))
690                 use_ssl = 0;
691         if (!strcmp(prot, "https") || !strcmp(prot, "wss"))
692                 if (!use_ssl)
693                         use_ssl = LCCSCF_USE_SSL;
694
695         lwsl_debug("'%s' %p '%s' %p\n", i.address, i.address, i.path, i.path);
696
697         /*
698          * create the websockets context.  This tracks open connections and
699          * knows how to route any traffic and which protocol version to use,
700          * and if each connection is client or server side.
701          *
702          * For this client-only demo, we tell it to not listen on any port.
703          */
704
705         info.port = CONTEXT_PORT_NO_LISTEN;
706         info.protocols = protocols;
707         info.gid = -1;
708         info.uid = -1;
709         info.ws_ping_pong_interval = pp_secs;
710         info.extensions = exts;
711
712         /*
713          * since we know this lws context is only ever going to be used with
714          * a few client wsis / fds / sockets at a time, let lws know it doesn't
715          * have to use the default allocations for fd tables up to ulimit -n.
716          * It will just allocate for 2 internal and 4 that we might use.
717          */
718         info.fd_limit_per_thread = 2 + 4;
719
720 #if defined(LWS_WITH_TLS)
721         info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
722 #endif
723
724         info.options |= LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
725
726         if (use_ssl) {
727                 /*
728                  * If the server wants us to present a valid SSL client certificate
729                  * then we can set it up here.
730                  */
731
732                 if (cert_path[0])
733                         info.client_ssl_cert_filepath = cert_path;
734                 if (key_path[0])
735                         info.client_ssl_private_key_filepath = key_path;
736
737                 /*
738                  * A CA cert and CRL can be used to validate the cert send by the server
739                  */
740                 if (ca_path[0])
741                         info.client_ssl_ca_filepath = ca_path;
742
743 #if defined(LWS_WITH_TLS) && defined(LWS_HAVE_SSL_CTX_set1_param)
744                 else if (crl_path[0])
745                         lwsl_notice("WARNING, providing a CRL requires a CA cert!\n");
746 #endif
747         }
748
749         if (use_ssl & LCCSCF_USE_SSL) {
750                 lwsl_notice(" Using SSL\n");
751 #if defined(LWS_WITH_MBEDTLS)
752                 lwsl_notice("   (NOTE: mbedtls needs to be given the remote\n");
753                 lwsl_notice("    CA cert to trust (with -A) to validate it)\n");
754 #endif
755         }
756         else
757                 lwsl_notice(" SSL disabled\n");
758         if (use_ssl & LCCSCF_ALLOW_SELFSIGNED)
759                 lwsl_notice(" Selfsigned certs allowed\n");
760         else
761                 lwsl_notice(" Cert must validate correctly (use -s to allow selfsigned)\n");
762         if (use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)
763                 lwsl_notice(" Skipping peer cert hostname check\n");
764         else
765                 lwsl_notice(" Requiring peer cert hostname matches\n");
766
767         context = lws_create_context(&info);
768         if (context == NULL) {
769                 fprintf(stderr, "Creating libwebsocket context failed\n");
770                 return 1;
771         }
772
773         i.context = context;
774         i.ssl_connection = use_ssl;
775         i.host = i.address;
776         i.origin = i.address;
777         i.ietf_version_or_minus_one = ietf_version;
778
779         if (!strcmp(prot, "http") || !strcmp(prot, "https")) {
780                 lwsl_notice("using %s mode (non-ws)\n", prot);
781                 if (test_post) {
782                         i.method = "POST";
783                         lwsl_notice("POST mode\n");
784                 }
785                 else
786                         i.method = "GET";
787                 do_ws = 0;
788         } else
789                 if (!strcmp(prot, "raw")) {
790                         i.method = "RAW";
791                         i.protocol = "lws-test-raw-client";
792                         lwsl_notice("using RAW mode connection\n");
793                         do_ws = 0;
794                 } else
795                         lwsl_notice("using %s mode (ws)\n", prot);
796
797         /*
798          * sit there servicing the websocket context to handle incoming
799          * packets, and drawing random circles on the mirror protocol websocket
800          *
801          * nothing happens until the client websocket connection is
802          * asynchronously established... calling lws_client_connect() only
803          * instantiates the connection logically, lws_service() progresses it
804          * asynchronously.
805          */
806
807         m = 0;
808         while (!force_exit) {
809
810                 if (do_multi) {
811                         for (n = 0; n < (int)LWS_ARRAY_SIZE(wsi_multi); n++) {
812                                 if (!wsi_multi[n] && ratelimit_connects(&rl_multi[n], 2u)) {
813                                         lwsl_notice("dumb %d: connecting\n", n);
814                                         i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
815                                         i.pwsi = &wsi_multi[n];
816                                         lws_client_connect_via_info(&i);
817                                 }
818                         }
819                 } else {
820
821                         if (do_ws) {
822                                 if (!flag_echo && !justmirror && !wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
823                                         lwsl_notice("dumb: connecting\n");
824                                         i.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
825                                         i.pwsi = &wsi_dumb;
826                                         lws_client_connect_via_info(&i);
827                                 }
828
829                                 if (!wsi_mirror && ratelimit_connects(&rl_mirror, 2u)) {
830                                         lwsl_notice("mirror: connecting\n");
831                                         i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
832                                         i.pwsi = &wsi_mirror;
833                                         wsi_mirror = lws_client_connect_via_info(&i);
834                                 }
835                         } else
836                                 if (!wsi_dumb && ratelimit_connects(&rl_dumb, 2u)) {
837                                         lwsl_notice("http: connecting\n");
838                                         i.pwsi = &wsi_dumb;
839                                         lws_client_connect_via_info(&i);
840                                 }
841                 }
842
843                 lws_service(context, 500);
844
845                 if (do_multi) {
846                         m++;
847                         if (m == 10) {
848                                 m = 0;
849                                 lwsl_notice("doing lws_callback_on_writable_all_protocol\n");
850                                 lws_callback_on_writable_all_protocol(context,
851                                            &protocols[PROTOCOL_DUMB_INCREMENT]);
852                         }
853                 }
854
855                 if (flag_echo && lws_now_secs() != last) {
856                         lwsl_notice("rxb %d, rx_count %d\n", rxb, rx_count);
857                         last = lws_now_secs();
858                 }
859         }
860
861         lwsl_err("Exiting\n");
862         lws_context_destroy(context);
863
864         return ret;
865
866 usage:
867         fprintf(stderr, "Usage: libwebsockets-test-client "
868                                 "<server address> [--port=<p>] "
869                                 "[--ssl] [-k] [-v <ver>] "
870                                 "[-d <log bitfield>] [-l]\n");
871         return 1;
872 }