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