1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * lws-minimal-ws-client-spam 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Written in 2010-2021 by Andy Green <andy@warmcat.com> 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0 7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication. 8d4afb5ceSopenharmony_ci * 9d4afb5ceSopenharmony_ci * This demonstrates a ws client that makes continuous mass ws connections 10d4afb5ceSopenharmony_ci * asynchronously 11d4afb5ceSopenharmony_ci */ 12d4afb5ceSopenharmony_ci 13d4afb5ceSopenharmony_ci#include <libwebsockets.h> 14d4afb5ceSopenharmony_ci#include <string.h> 15d4afb5ceSopenharmony_ci#include <signal.h> 16d4afb5ceSopenharmony_ci#if defined(WIN32) 17d4afb5ceSopenharmony_ci#define HAVE_STRUCT_TIMESPEC 18d4afb5ceSopenharmony_ci#if defined(pid_t) 19d4afb5ceSopenharmony_ci#undef pid_t 20d4afb5ceSopenharmony_ci#endif 21d4afb5ceSopenharmony_ci#endif 22d4afb5ceSopenharmony_ci#include <pthread.h> 23d4afb5ceSopenharmony_ci 24d4afb5ceSopenharmony_cienum { 25d4afb5ceSopenharmony_ci CLIENT_IDLE, 26d4afb5ceSopenharmony_ci CLIENT_CONNECTING, 27d4afb5ceSopenharmony_ci CLIENT_AWAITING_SEND, 28d4afb5ceSopenharmony_ci}; 29d4afb5ceSopenharmony_ci 30d4afb5ceSopenharmony_cistruct client { 31d4afb5ceSopenharmony_ci struct lws *wsi; 32d4afb5ceSopenharmony_ci int index; 33d4afb5ceSopenharmony_ci int state; 34d4afb5ceSopenharmony_ci}; 35d4afb5ceSopenharmony_ci 36d4afb5ceSopenharmony_cistatic struct lws_context *context; 37d4afb5ceSopenharmony_cistatic struct client clients[200]; 38d4afb5ceSopenharmony_cistatic int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL; 39d4afb5ceSopenharmony_cistatic const char *server_address = "libwebsockets.org", 40d4afb5ceSopenharmony_ci *pro = "lws-mirror-protocol"; 41d4afb5ceSopenharmony_cistatic int concurrent = 3, conn, tries, est, errors, closed, sent, limit = 15; 42d4afb5ceSopenharmony_ci 43d4afb5ceSopenharmony_cistruct pss { 44d4afb5ceSopenharmony_ci int conn; 45d4afb5ceSopenharmony_ci}; 46d4afb5ceSopenharmony_ci 47d4afb5ceSopenharmony_cistatic int 48d4afb5ceSopenharmony_ciconnect_client(int idx) 49d4afb5ceSopenharmony_ci{ 50d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 51d4afb5ceSopenharmony_ci 52d4afb5ceSopenharmony_ci if (tries == limit) { 53d4afb5ceSopenharmony_ci lwsl_user("Reached limit... finishing\n"); 54d4afb5ceSopenharmony_ci return 0; 55d4afb5ceSopenharmony_ci } 56d4afb5ceSopenharmony_ci 57d4afb5ceSopenharmony_ci memset(&i, 0, sizeof(i)); 58d4afb5ceSopenharmony_ci 59d4afb5ceSopenharmony_ci i.context = context; 60d4afb5ceSopenharmony_ci i.port = port; 61d4afb5ceSopenharmony_ci i.address = server_address; 62d4afb5ceSopenharmony_ci i.path = "/"; 63d4afb5ceSopenharmony_ci i.host = i.address; 64d4afb5ceSopenharmony_ci i.origin = i.address; 65d4afb5ceSopenharmony_ci i.ssl_connection = ssl_connection; 66d4afb5ceSopenharmony_ci i.protocol = pro; 67d4afb5ceSopenharmony_ci i.local_protocol_name = pro; 68d4afb5ceSopenharmony_ci i.pwsi = &clients[idx].wsi; 69d4afb5ceSopenharmony_ci 70d4afb5ceSopenharmony_ci clients[idx].state = CLIENT_CONNECTING; 71d4afb5ceSopenharmony_ci tries++; 72d4afb5ceSopenharmony_ci 73d4afb5ceSopenharmony_ci lwsl_notice("%s: connection %s:%d\n", __func__, i.address, i.port); 74d4afb5ceSopenharmony_ci if (!lws_client_connect_via_info(&i)) { 75d4afb5ceSopenharmony_ci clients[idx].wsi = NULL; 76d4afb5ceSopenharmony_ci clients[idx].state = CLIENT_IDLE; 77d4afb5ceSopenharmony_ci 78d4afb5ceSopenharmony_ci return 1; 79d4afb5ceSopenharmony_ci } 80d4afb5ceSopenharmony_ci 81d4afb5ceSopenharmony_ci return 0; 82d4afb5ceSopenharmony_ci} 83d4afb5ceSopenharmony_ci 84d4afb5ceSopenharmony_cistatic int 85d4afb5ceSopenharmony_cicallback_minimal_spam(struct lws *wsi, enum lws_callback_reasons reason, 86d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 87d4afb5ceSopenharmony_ci{ 88d4afb5ceSopenharmony_ci struct pss *pss = (struct pss *)user; 89d4afb5ceSopenharmony_ci uint8_t ping[LWS_PRE + 125]; 90d4afb5ceSopenharmony_ci int n, m; 91d4afb5ceSopenharmony_ci 92d4afb5ceSopenharmony_ci switch (reason) { 93d4afb5ceSopenharmony_ci 94d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_INIT: 95d4afb5ceSopenharmony_ci for (n = 0; n < concurrent; n++) { 96d4afb5ceSopenharmony_ci clients[n].index = n; 97d4afb5ceSopenharmony_ci connect_client(n); 98d4afb5ceSopenharmony_ci } 99d4afb5ceSopenharmony_ci break; 100d4afb5ceSopenharmony_ci 101d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 102d4afb5ceSopenharmony_ci errors++; 103d4afb5ceSopenharmony_ci lwsl_err("CLIENT_CONNECTION_ERROR: %s (try %d, est %d, closed %d, err %d)\n", 104d4afb5ceSopenharmony_ci in ? (char *)in : "(null)", tries, est, closed, errors); 105d4afb5ceSopenharmony_ci for (n = 0; n < concurrent; n++) { 106d4afb5ceSopenharmony_ci if (clients[n].wsi == wsi) { 107d4afb5ceSopenharmony_ci clients[n].wsi = NULL; 108d4afb5ceSopenharmony_ci clients[n].state = CLIENT_IDLE; 109d4afb5ceSopenharmony_ci connect_client(n); 110d4afb5ceSopenharmony_ci break; 111d4afb5ceSopenharmony_ci } 112d4afb5ceSopenharmony_ci } 113d4afb5ceSopenharmony_ci if (tries == closed + errors) { 114d4afb5ceSopenharmony_ci interrupted = 1; 115d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); 116d4afb5ceSopenharmony_ci } 117d4afb5ceSopenharmony_ci break; 118d4afb5ceSopenharmony_ci 119d4afb5ceSopenharmony_ci /* --- client callbacks --- */ 120d4afb5ceSopenharmony_ci 121d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_ESTABLISHED: 122d4afb5ceSopenharmony_ci lwsl_user("%s: established (try %d, est %d, closed %d, err %d)\n", 123d4afb5ceSopenharmony_ci __func__, tries, est, closed, errors); 124d4afb5ceSopenharmony_ci est++; 125d4afb5ceSopenharmony_ci pss->conn = conn++; 126d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 127d4afb5ceSopenharmony_ci break; 128d4afb5ceSopenharmony_ci 129d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CLOSED: 130d4afb5ceSopenharmony_ci closed++; 131d4afb5ceSopenharmony_ci if (tries == closed + errors) { 132d4afb5ceSopenharmony_ci interrupted = 1; 133d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); 134d4afb5ceSopenharmony_ci } 135d4afb5ceSopenharmony_ci if (tries == limit) { 136d4afb5ceSopenharmony_ci lwsl_user("%s: leaving CLOSED (try %d, est %d, sent %d, closed %d, err %d)\n", 137d4afb5ceSopenharmony_ci __func__, tries, est, sent, closed, errors); 138d4afb5ceSopenharmony_ci break; 139d4afb5ceSopenharmony_ci } 140d4afb5ceSopenharmony_ci 141d4afb5ceSopenharmony_ci for (n = 0; n < concurrent; n++) { 142d4afb5ceSopenharmony_ci if (clients[n].wsi == wsi) { 143d4afb5ceSopenharmony_ci connect_client(n); 144d4afb5ceSopenharmony_ci lwsl_user("%s: reopening (try %d, est %d, closed %d, err %d)\n", 145d4afb5ceSopenharmony_ci __func__, tries, est, closed, errors); 146d4afb5ceSopenharmony_ci break; 147d4afb5ceSopenharmony_ci } 148d4afb5ceSopenharmony_ci } 149d4afb5ceSopenharmony_ci if (n == concurrent) 150d4afb5ceSopenharmony_ci lwsl_user("CLOSED: can't find client wsi\n"); 151d4afb5ceSopenharmony_ci break; 152d4afb5ceSopenharmony_ci 153d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_WRITEABLE: 154d4afb5ceSopenharmony_ci n = lws_snprintf((char *)ping + LWS_PRE, sizeof(ping) - LWS_PRE, 155d4afb5ceSopenharmony_ci "hello %d", pss->conn); 156d4afb5ceSopenharmony_ci 157d4afb5ceSopenharmony_ci m = lws_write(wsi, ping + LWS_PRE, (unsigned int)n, LWS_WRITE_TEXT); 158d4afb5ceSopenharmony_ci if (m < n) { 159d4afb5ceSopenharmony_ci lwsl_err("sending ping failed: %d\n", m); 160d4afb5ceSopenharmony_ci 161d4afb5ceSopenharmony_ci return -1; 162d4afb5ceSopenharmony_ci } 163d4afb5ceSopenharmony_ci lws_set_timeout(wsi, PENDING_TIMEOUT_USER_OK, LWS_TO_KILL_ASYNC); 164d4afb5ceSopenharmony_ci break; 165d4afb5ceSopenharmony_ci 166d4afb5ceSopenharmony_ci default: 167d4afb5ceSopenharmony_ci break; 168d4afb5ceSopenharmony_ci } 169d4afb5ceSopenharmony_ci 170d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 171d4afb5ceSopenharmony_ci} 172d4afb5ceSopenharmony_ci 173d4afb5ceSopenharmony_cistatic const struct lws_protocols protocols[] = { 174d4afb5ceSopenharmony_ci { 175d4afb5ceSopenharmony_ci "lws-spam-test", 176d4afb5ceSopenharmony_ci callback_minimal_spam, 177d4afb5ceSopenharmony_ci sizeof(struct pss), 178d4afb5ceSopenharmony_ci 0, 0, NULL, 0 179d4afb5ceSopenharmony_ci }, 180d4afb5ceSopenharmony_ci LWS_PROTOCOL_LIST_TERM 181d4afb5ceSopenharmony_ci}; 182d4afb5ceSopenharmony_ci 183d4afb5ceSopenharmony_cistatic struct lws_protocol_vhost_options pvo = { 184d4afb5ceSopenharmony_ci NULL, /* "next" pvo linked-list */ 185d4afb5ceSopenharmony_ci NULL, /* "child" pvo linked-list */ 186d4afb5ceSopenharmony_ci "lws-spam-test", /* protocol name we belong to on this vhost */ 187d4afb5ceSopenharmony_ci "OK" /* ignored */ 188d4afb5ceSopenharmony_ci}; 189d4afb5ceSopenharmony_ci 190d4afb5ceSopenharmony_cistatic void 191d4afb5ceSopenharmony_cisigint_handler(int sig) 192d4afb5ceSopenharmony_ci{ 193d4afb5ceSopenharmony_ci interrupted = 1; 194d4afb5ceSopenharmony_ci} 195d4afb5ceSopenharmony_ci 196d4afb5ceSopenharmony_ciint main(int argc, const char **argv) 197d4afb5ceSopenharmony_ci{ 198d4afb5ceSopenharmony_ci struct lws_context_creation_info info; 199d4afb5ceSopenharmony_ci const char *p; 200d4afb5ceSopenharmony_ci int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE 201d4afb5ceSopenharmony_ci /* for LLL_ verbosity above NOTICE to be built into lws, 202d4afb5ceSopenharmony_ci * lws must have been configured and built with 203d4afb5ceSopenharmony_ci * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ 204d4afb5ceSopenharmony_ci /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ 205d4afb5ceSopenharmony_ci /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ 206d4afb5ceSopenharmony_ci /* | LLL_DEBUG */; 207d4afb5ceSopenharmony_ci 208d4afb5ceSopenharmony_ci signal(SIGINT, sigint_handler); 209d4afb5ceSopenharmony_ci 210d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-d"))) 211d4afb5ceSopenharmony_ci logs = atoi(p); 212d4afb5ceSopenharmony_ci 213d4afb5ceSopenharmony_ci lws_set_log_level(logs, NULL); 214d4afb5ceSopenharmony_ci lwsl_user("LWS minimal ws client SPAM\n"); 215d4afb5ceSopenharmony_ci 216d4afb5ceSopenharmony_ci memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 217d4afb5ceSopenharmony_ci info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 218d4afb5ceSopenharmony_ci info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ 219d4afb5ceSopenharmony_ci info.protocols = protocols; 220d4afb5ceSopenharmony_ci info.pvo = &pvo; 221d4afb5ceSopenharmony_ci#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL) 222d4afb5ceSopenharmony_ci /* 223d4afb5ceSopenharmony_ci * OpenSSL uses the system trust store. mbedTLS has to be told which 224d4afb5ceSopenharmony_ci * CA to trust explicitly. 225d4afb5ceSopenharmony_ci */ 226d4afb5ceSopenharmony_ci info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; 227d4afb5ceSopenharmony_ci#endif 228d4afb5ceSopenharmony_ci 229d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--server"))) { 230d4afb5ceSopenharmony_ci server_address = p; 231d4afb5ceSopenharmony_ci ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; 232d4afb5ceSopenharmony_ci } 233d4afb5ceSopenharmony_ci 234d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--port"))) 235d4afb5ceSopenharmony_ci port = atoi(p); 236d4afb5ceSopenharmony_ci 237d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-l"))) 238d4afb5ceSopenharmony_ci limit = atoi(p); 239d4afb5ceSopenharmony_ci 240d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-c"))) 241d4afb5ceSopenharmony_ci concurrent = atoi(p); 242d4afb5ceSopenharmony_ci 243d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "-n")) { 244d4afb5ceSopenharmony_ci ssl_connection = 0; 245d4afb5ceSopenharmony_ci info.options = 0; 246d4afb5ceSopenharmony_ci } 247d4afb5ceSopenharmony_ci 248d4afb5ceSopenharmony_ci if (concurrent < 0 || 249d4afb5ceSopenharmony_ci concurrent > (int)LWS_ARRAY_SIZE(clients)) { 250d4afb5ceSopenharmony_ci lwsl_err("%s: -c %d larger than max concurrency %d\n", __func__, 251d4afb5ceSopenharmony_ci concurrent, (int)LWS_ARRAY_SIZE(clients)); 252d4afb5ceSopenharmony_ci 253d4afb5ceSopenharmony_ci return 1; 254d4afb5ceSopenharmony_ci } 255d4afb5ceSopenharmony_ci 256d4afb5ceSopenharmony_ci /* 257d4afb5ceSopenharmony_ci * since we know this lws context is only ever going to be used with 258d4afb5ceSopenharmony_ci * one client wsis / fds / sockets at a time, let lws know it doesn't 259d4afb5ceSopenharmony_ci * have to use the default allocations for fd tables up to ulimit -n. 260d4afb5ceSopenharmony_ci * It will just allocate for 1 internal and n (+ 1 http2 nwsi) that we 261d4afb5ceSopenharmony_ci * will use. 262d4afb5ceSopenharmony_ci */ 263d4afb5ceSopenharmony_ci info.fd_limit_per_thread = (unsigned int)(1 + concurrent + 1); 264d4afb5ceSopenharmony_ci 265d4afb5ceSopenharmony_ci context = lws_create_context(&info); 266d4afb5ceSopenharmony_ci if (!context) { 267d4afb5ceSopenharmony_ci lwsl_err("lws init failed\n"); 268d4afb5ceSopenharmony_ci return 1; 269d4afb5ceSopenharmony_ci } 270d4afb5ceSopenharmony_ci 271d4afb5ceSopenharmony_ci while (n >= 0 && !interrupted) 272d4afb5ceSopenharmony_ci n = lws_service(context, 0); 273d4afb5ceSopenharmony_ci 274d4afb5ceSopenharmony_ci lwsl_notice("%s: exiting service loop\n", __func__); 275d4afb5ceSopenharmony_ci 276d4afb5ceSopenharmony_ci lws_context_destroy(context); 277d4afb5ceSopenharmony_ci 278d4afb5ceSopenharmony_ci if (tries == limit && closed == tries) { 279d4afb5ceSopenharmony_ci lwsl_user("Completed\n"); 280d4afb5ceSopenharmony_ci return 0; 281d4afb5ceSopenharmony_ci } 282d4afb5ceSopenharmony_ci 283d4afb5ceSopenharmony_ci lwsl_err("Failed\n"); 284d4afb5ceSopenharmony_ci 285d4afb5ceSopenharmony_ci return 1; 286d4afb5ceSopenharmony_ci} 287