1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * lws-minimal-http-client-multi 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 the a minimal http client using lws, which makes 10d4afb5ceSopenharmony_ci * 8 downloads simultaneously from warmcat.com. 11d4afb5ceSopenharmony_ci * 12d4afb5ceSopenharmony_ci * Currently that takes the form of 8 individual simultaneous tcp and 13d4afb5ceSopenharmony_ci * tls connections, which happen concurrently. Notice that the ordering 14d4afb5ceSopenharmony_ci * of the returned payload may be intermingled for the various connections. 15d4afb5ceSopenharmony_ci * 16d4afb5ceSopenharmony_ci * By default the connections happen all together at the beginning and operate 17d4afb5ceSopenharmony_ci * concurrently, which is fast. However this is resource-intenstive, there are 18d4afb5ceSopenharmony_ci * 8 tcp connections, 8 tls tunnels on both the client and server. You can 19d4afb5ceSopenharmony_ci * instead opt to have the connections happen one after the other inside a 20d4afb5ceSopenharmony_ci * single tcp connection and tls tunnel, using HTTP/1.1 pipelining. To be 21d4afb5ceSopenharmony_ci * eligible to be pipelined on another existing connection to the same server, 22d4afb5ceSopenharmony_ci * the client connection must have the LCCSCF_PIPELINE flag on its 23d4afb5ceSopenharmony_ci * info.ssl_connection member (this is independent of whether the connection 24d4afb5ceSopenharmony_ci * is in ssl mode or not). 25d4afb5ceSopenharmony_ci * 26d4afb5ceSopenharmony_ci * HTTP/1.0: Pipelining only possible if Keep-Alive: yes sent by server 27d4afb5ceSopenharmony_ci * HTTP/1.1: always possible... serializes requests 28d4afb5ceSopenharmony_ci * HTTP/2: always possible... all requests sent as individual streams in parallel 29d4afb5ceSopenharmony_ci * 30d4afb5ceSopenharmony_ci * Note: stats are kept on tls session reuse and checked depending on mode 31d4afb5ceSopenharmony_ci * 32d4afb5ceSopenharmony_ci * - default: no reuse expected (connections made too quickly at once) 33d4afb5ceSopenharmony_ci * - staggered, no pipeline: n - 1 reuse expected 34d4afb5ceSopenharmony_ci * - staggered, pipelined: no reuse expected 35d4afb5ceSopenharmony_ci */ 36d4afb5ceSopenharmony_ci 37d4afb5ceSopenharmony_ci#include <libwebsockets.h> 38d4afb5ceSopenharmony_ci#include <string.h> 39d4afb5ceSopenharmony_ci#include <signal.h> 40d4afb5ceSopenharmony_ci#include <assert.h> 41d4afb5ceSopenharmony_ci#include <time.h> 42d4afb5ceSopenharmony_ci#if !defined(WIN32) 43d4afb5ceSopenharmony_ci#include <sys/stat.h> 44d4afb5ceSopenharmony_ci#include <fcntl.h> 45d4afb5ceSopenharmony_ci#include <unistd.h> 46d4afb5ceSopenharmony_ci#endif 47d4afb5ceSopenharmony_ci 48d4afb5ceSopenharmony_ci#define COUNT 8 49d4afb5ceSopenharmony_ci 50d4afb5ceSopenharmony_cistruct cliuser { 51d4afb5ceSopenharmony_ci int index; 52d4afb5ceSopenharmony_ci}; 53d4afb5ceSopenharmony_ci 54d4afb5ceSopenharmony_cistatic int completed, failed, numbered, stagger_idx, posting, count = COUNT, 55d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) 56d4afb5ceSopenharmony_ci reuse, 57d4afb5ceSopenharmony_ci#endif 58d4afb5ceSopenharmony_ci staggered; 59d4afb5ceSopenharmony_cistatic lws_sorted_usec_list_t sul_stagger; 60d4afb5ceSopenharmony_cistatic struct lws_client_connect_info i; 61d4afb5ceSopenharmony_cistatic struct lws *client_wsi[COUNT]; 62d4afb5ceSopenharmony_cistatic char urlpath[64], intr; 63d4afb5ceSopenharmony_cistatic struct lws_context *context; 64d4afb5ceSopenharmony_ci 65d4afb5ceSopenharmony_ci/* we only need this for tracking POST emit state */ 66d4afb5ceSopenharmony_ci 67d4afb5ceSopenharmony_cistruct pss { 68d4afb5ceSopenharmony_ci char body_part; 69d4afb5ceSopenharmony_ci}; 70d4afb5ceSopenharmony_ci 71d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) 72d4afb5ceSopenharmony_ci 73d4afb5ceSopenharmony_ci/* this should work OK on win32, but not adapted for non-posix file apis */ 74d4afb5ceSopenharmony_ci 75d4afb5ceSopenharmony_cistatic int 76d4afb5ceSopenharmony_cisess_save_cb(struct lws_context *cx, struct lws_tls_session_dump *info) 77d4afb5ceSopenharmony_ci{ 78d4afb5ceSopenharmony_ci char path[128]; 79d4afb5ceSopenharmony_ci int fd, n; 80d4afb5ceSopenharmony_ci 81d4afb5ceSopenharmony_ci lws_snprintf(path, sizeof(path), "%s/lws_tls_sess_%s", (const char *)info->opaque, 82d4afb5ceSopenharmony_ci info->tag); 83d4afb5ceSopenharmony_ci fd = open(path, LWS_O_WRONLY | O_CREAT | O_TRUNC, 0600); 84d4afb5ceSopenharmony_ci if (fd < 0) { 85d4afb5ceSopenharmony_ci lwsl_warn("%s: cannot open %s\n", __func__, path); 86d4afb5ceSopenharmony_ci return 1; 87d4afb5ceSopenharmony_ci } 88d4afb5ceSopenharmony_ci 89d4afb5ceSopenharmony_ci n = (int)write(fd, info->blob, info->blob_len); 90d4afb5ceSopenharmony_ci 91d4afb5ceSopenharmony_ci close(fd); 92d4afb5ceSopenharmony_ci 93d4afb5ceSopenharmony_ci return n != (int)info->blob_len; 94d4afb5ceSopenharmony_ci} 95d4afb5ceSopenharmony_ci 96d4afb5ceSopenharmony_cistatic int 97d4afb5ceSopenharmony_cisess_load_cb(struct lws_context *cx, struct lws_tls_session_dump *info) 98d4afb5ceSopenharmony_ci{ 99d4afb5ceSopenharmony_ci struct stat sta; 100d4afb5ceSopenharmony_ci char path[128]; 101d4afb5ceSopenharmony_ci int fd, n; 102d4afb5ceSopenharmony_ci 103d4afb5ceSopenharmony_ci lws_snprintf(path, sizeof(path), "%s/lws_tls_sess_%s", (const char *)info->opaque, 104d4afb5ceSopenharmony_ci info->tag); 105d4afb5ceSopenharmony_ci fd = open(path, LWS_O_RDONLY); 106d4afb5ceSopenharmony_ci if (fd < 0) 107d4afb5ceSopenharmony_ci return 1; 108d4afb5ceSopenharmony_ci 109d4afb5ceSopenharmony_ci if (fstat(fd, &sta) || !sta.st_size) 110d4afb5ceSopenharmony_ci goto bail; 111d4afb5ceSopenharmony_ci 112d4afb5ceSopenharmony_ci info->blob = malloc((size_t)sta.st_size); 113d4afb5ceSopenharmony_ci /* caller will free this */ 114d4afb5ceSopenharmony_ci if (!info->blob) 115d4afb5ceSopenharmony_ci goto bail; 116d4afb5ceSopenharmony_ci 117d4afb5ceSopenharmony_ci info->blob_len = (size_t)sta.st_size; 118d4afb5ceSopenharmony_ci 119d4afb5ceSopenharmony_ci n = (int)read(fd, info->blob, info->blob_len); 120d4afb5ceSopenharmony_ci close(fd); 121d4afb5ceSopenharmony_ci 122d4afb5ceSopenharmony_ci return n != (int)info->blob_len; 123d4afb5ceSopenharmony_ci 124d4afb5ceSopenharmony_cibail: 125d4afb5ceSopenharmony_ci close(fd); 126d4afb5ceSopenharmony_ci 127d4afb5ceSopenharmony_ci return 1; 128d4afb5ceSopenharmony_ci} 129d4afb5ceSopenharmony_ci#endif 130d4afb5ceSopenharmony_ci 131d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CONMON) 132d4afb5ceSopenharmony_civoid 133d4afb5ceSopenharmony_cidump_conmon_data(struct lws *wsi) 134d4afb5ceSopenharmony_ci{ 135d4afb5ceSopenharmony_ci const struct addrinfo *ai; 136d4afb5ceSopenharmony_ci struct lws_conmon cm; 137d4afb5ceSopenharmony_ci char ads[48]; 138d4afb5ceSopenharmony_ci 139d4afb5ceSopenharmony_ci lws_conmon_wsi_take(wsi, &cm); 140d4afb5ceSopenharmony_ci 141d4afb5ceSopenharmony_ci lws_sa46_write_numeric_address(&cm.peer46, ads, sizeof(ads)); 142d4afb5ceSopenharmony_ci lwsl_notice("%s: peer %s, dns: %uus, sockconn: %uus, tls: %uus, txn_resp: %uus\n", 143d4afb5ceSopenharmony_ci __func__, ads, 144d4afb5ceSopenharmony_ci (unsigned int)cm.ciu_dns, 145d4afb5ceSopenharmony_ci (unsigned int)cm.ciu_sockconn, 146d4afb5ceSopenharmony_ci (unsigned int)cm.ciu_tls, 147d4afb5ceSopenharmony_ci (unsigned int)cm.ciu_txn_resp); 148d4afb5ceSopenharmony_ci 149d4afb5ceSopenharmony_ci ai = cm.dns_results_copy; 150d4afb5ceSopenharmony_ci while (ai) { 151d4afb5ceSopenharmony_ci lws_sa46_write_numeric_address((lws_sockaddr46 *)ai->ai_addr, ads, sizeof(ads)); 152d4afb5ceSopenharmony_ci lwsl_notice("%s: DNS %s\n", __func__, ads); 153d4afb5ceSopenharmony_ci ai = ai->ai_next; 154d4afb5ceSopenharmony_ci } 155d4afb5ceSopenharmony_ci 156d4afb5ceSopenharmony_ci /* 157d4afb5ceSopenharmony_ci * This destroys the DNS list in the lws_conmon that we took 158d4afb5ceSopenharmony_ci * responsibility for when we used lws_conmon_wsi_take() 159d4afb5ceSopenharmony_ci */ 160d4afb5ceSopenharmony_ci 161d4afb5ceSopenharmony_ci lws_conmon_release(&cm); 162d4afb5ceSopenharmony_ci} 163d4afb5ceSopenharmony_ci#endif 164d4afb5ceSopenharmony_ci 165d4afb5ceSopenharmony_cistatic int 166d4afb5ceSopenharmony_cicallback_http(struct lws *wsi, enum lws_callback_reasons reason, 167d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 168d4afb5ceSopenharmony_ci{ 169d4afb5ceSopenharmony_ci char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, 170d4afb5ceSopenharmony_ci *end = &buf[sizeof(buf) - 1]; 171d4afb5ceSopenharmony_ci int n, idx = (int)(intptr_t)lws_get_opaque_user_data(wsi); 172d4afb5ceSopenharmony_ci struct pss *pss = (struct pss *)user; 173d4afb5ceSopenharmony_ci 174d4afb5ceSopenharmony_ci switch (reason) { 175d4afb5ceSopenharmony_ci 176d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: 177d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u\n", 178d4afb5ceSopenharmony_ci idx, lws_http_client_http_response(wsi)); 179d4afb5ceSopenharmony_ci 180d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) 181d4afb5ceSopenharmony_ci if (lws_tls_session_is_reused(wsi)) 182d4afb5ceSopenharmony_ci reuse++; 183d4afb5ceSopenharmony_ci else 184d4afb5ceSopenharmony_ci /* 185d4afb5ceSopenharmony_ci * Attempt to store any new session into 186d4afb5ceSopenharmony_ci * external storage 187d4afb5ceSopenharmony_ci */ 188d4afb5ceSopenharmony_ci if (lws_tls_session_dump_save(lws_get_vhost_by_name(context, "default"), 189d4afb5ceSopenharmony_ci i.host, (uint16_t)i.port, 190d4afb5ceSopenharmony_ci sess_save_cb, "/tmp")) 191d4afb5ceSopenharmony_ci lwsl_warn("%s: session save failed\n", __func__); 192d4afb5ceSopenharmony_ci#endif 193d4afb5ceSopenharmony_ci break; 194d4afb5ceSopenharmony_ci 195d4afb5ceSopenharmony_ci /* because we are protocols[0] ... */ 196d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 197d4afb5ceSopenharmony_ci lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", 198d4afb5ceSopenharmony_ci in ? (char *)in : "(null)"); 199d4afb5ceSopenharmony_ci client_wsi[idx] = NULL; 200d4afb5ceSopenharmony_ci failed++; 201d4afb5ceSopenharmony_ci 202d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CONMON) 203d4afb5ceSopenharmony_ci dump_conmon_data(wsi); 204d4afb5ceSopenharmony_ci#endif 205d4afb5ceSopenharmony_ci 206d4afb5ceSopenharmony_ci goto finished; 207d4afb5ceSopenharmony_ci 208d4afb5ceSopenharmony_ci /* chunks of chunked content, with header removed */ 209d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: 210d4afb5ceSopenharmony_ci lwsl_user("RECEIVE_CLIENT_HTTP_READ: conn %d: read %d\n", idx, (int)len); 211d4afb5ceSopenharmony_ci lwsl_hexdump_info(in, len); 212d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 213d4afb5ceSopenharmony_ci 214d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: 215d4afb5ceSopenharmony_ci 216d4afb5ceSopenharmony_ci /* 217d4afb5ceSopenharmony_ci * Tell lws we are going to send the body next... 218d4afb5ceSopenharmony_ci */ 219d4afb5ceSopenharmony_ci if (posting && !lws_http_is_redirected_to_get(wsi)) { 220d4afb5ceSopenharmony_ci lwsl_user("%s: conn %d, doing POST flow\n", __func__, idx); 221d4afb5ceSopenharmony_ci lws_client_http_body_pending(wsi, 1); 222d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 223d4afb5ceSopenharmony_ci } else 224d4afb5ceSopenharmony_ci lwsl_user("%s: conn %d, doing GET flow\n", __func__, idx); 225d4afb5ceSopenharmony_ci break; 226d4afb5ceSopenharmony_ci 227d4afb5ceSopenharmony_ci /* uninterpreted http content */ 228d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: 229d4afb5ceSopenharmony_ci { 230d4afb5ceSopenharmony_ci char buffer[1024 + LWS_PRE]; 231d4afb5ceSopenharmony_ci char *px = buffer + LWS_PRE; 232d4afb5ceSopenharmony_ci int lenx = sizeof(buffer) - LWS_PRE; 233d4afb5ceSopenharmony_ci 234d4afb5ceSopenharmony_ci if (lws_http_client_read(wsi, &px, &lenx) < 0) 235d4afb5ceSopenharmony_ci return -1; 236d4afb5ceSopenharmony_ci } 237d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 238d4afb5ceSopenharmony_ci 239d4afb5ceSopenharmony_ci case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: 240d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %s: idx %d\n", 241d4afb5ceSopenharmony_ci lws_wsi_tag(wsi), idx); 242d4afb5ceSopenharmony_ci client_wsi[idx] = NULL; 243d4afb5ceSopenharmony_ci goto finished; 244d4afb5ceSopenharmony_ci 245d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED_CLIENT_HTTP: 246d4afb5ceSopenharmony_ci lwsl_info("%s: closed: %s\n", __func__, lws_wsi_tag(client_wsi[idx])); 247d4afb5ceSopenharmony_ci 248d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CONMON) 249d4afb5ceSopenharmony_ci dump_conmon_data(wsi); 250d4afb5ceSopenharmony_ci#endif 251d4afb5ceSopenharmony_ci 252d4afb5ceSopenharmony_ci if (client_wsi[idx]) { 253d4afb5ceSopenharmony_ci /* 254d4afb5ceSopenharmony_ci * If it completed normally, it will have been set to 255d4afb5ceSopenharmony_ci * NULL then already. So we are dealing with an 256d4afb5ceSopenharmony_ci * abnormal, failing, close 257d4afb5ceSopenharmony_ci */ 258d4afb5ceSopenharmony_ci client_wsi[idx] = NULL; 259d4afb5ceSopenharmony_ci failed++; 260d4afb5ceSopenharmony_ci goto finished; 261d4afb5ceSopenharmony_ci } 262d4afb5ceSopenharmony_ci break; 263d4afb5ceSopenharmony_ci 264d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: 265d4afb5ceSopenharmony_ci if (!posting) 266d4afb5ceSopenharmony_ci break; 267d4afb5ceSopenharmony_ci if (lws_http_is_redirected_to_get(wsi)) 268d4afb5ceSopenharmony_ci break; 269d4afb5ceSopenharmony_ci lwsl_info("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: %s, idx %d," 270d4afb5ceSopenharmony_ci " part %d\n", lws_wsi_tag(wsi), idx, pss->body_part); 271d4afb5ceSopenharmony_ci 272d4afb5ceSopenharmony_ci n = LWS_WRITE_HTTP; 273d4afb5ceSopenharmony_ci 274d4afb5ceSopenharmony_ci /* 275d4afb5ceSopenharmony_ci * For a small body like this, we could prepare it in memory and 276d4afb5ceSopenharmony_ci * send it all at once. But to show how to handle, eg, 277d4afb5ceSopenharmony_ci * arbitrary-sized file payloads, or huge form-data fields, the 278d4afb5ceSopenharmony_ci * sending is done in multiple passes through the event loop. 279d4afb5ceSopenharmony_ci */ 280d4afb5ceSopenharmony_ci 281d4afb5ceSopenharmony_ci switch (pss->body_part++) { 282d4afb5ceSopenharmony_ci case 0: 283d4afb5ceSopenharmony_ci if (lws_client_http_multipart(wsi, "text", NULL, NULL, 284d4afb5ceSopenharmony_ci &p, end)) 285d4afb5ceSopenharmony_ci return -1; 286d4afb5ceSopenharmony_ci /* notice every usage of the boundary starts with -- */ 287d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "my text field\xd\xa"); 288d4afb5ceSopenharmony_ci break; 289d4afb5ceSopenharmony_ci case 1: 290d4afb5ceSopenharmony_ci if (lws_client_http_multipart(wsi, "file", "myfile.txt", 291d4afb5ceSopenharmony_ci "text/plain", &p, end)) 292d4afb5ceSopenharmony_ci return -1; 293d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), 294d4afb5ceSopenharmony_ci "This is the contents of the " 295d4afb5ceSopenharmony_ci "uploaded file.\xd\xa" 296d4afb5ceSopenharmony_ci "\xd\xa"); 297d4afb5ceSopenharmony_ci break; 298d4afb5ceSopenharmony_ci case 2: 299d4afb5ceSopenharmony_ci if (lws_client_http_multipart(wsi, NULL, NULL, NULL, 300d4afb5ceSopenharmony_ci &p, end)) 301d4afb5ceSopenharmony_ci return -1; 302d4afb5ceSopenharmony_ci lws_client_http_body_pending(wsi, 0); 303d4afb5ceSopenharmony_ci /* necessary to support H2, it means we will write no 304d4afb5ceSopenharmony_ci * more on this stream */ 305d4afb5ceSopenharmony_ci n = LWS_WRITE_HTTP_FINAL; 306d4afb5ceSopenharmony_ci break; 307d4afb5ceSopenharmony_ci 308d4afb5ceSopenharmony_ci default: 309d4afb5ceSopenharmony_ci /* 310d4afb5ceSopenharmony_ci * We can get extra callbacks here, if nothing to do, 311d4afb5ceSopenharmony_ci * then do nothing. 312d4afb5ceSopenharmony_ci */ 313d4afb5ceSopenharmony_ci return 0; 314d4afb5ceSopenharmony_ci } 315d4afb5ceSopenharmony_ci 316d4afb5ceSopenharmony_ci if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff_size_t(p, start), (enum lws_write_protocol)n) 317d4afb5ceSopenharmony_ci != lws_ptr_diff(p, start)) 318d4afb5ceSopenharmony_ci return 1; 319d4afb5ceSopenharmony_ci 320d4afb5ceSopenharmony_ci if (n != LWS_WRITE_HTTP_FINAL) 321d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 322d4afb5ceSopenharmony_ci 323d4afb5ceSopenharmony_ci break; 324d4afb5ceSopenharmony_ci 325d4afb5ceSopenharmony_ci default: 326d4afb5ceSopenharmony_ci break; 327d4afb5ceSopenharmony_ci } 328d4afb5ceSopenharmony_ci 329d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 330d4afb5ceSopenharmony_ci 331d4afb5ceSopenharmony_cifinished: 332d4afb5ceSopenharmony_ci if (++completed == count) { 333d4afb5ceSopenharmony_ci if (!failed) 334d4afb5ceSopenharmony_ci lwsl_user("Done: all OK\n"); 335d4afb5ceSopenharmony_ci else 336d4afb5ceSopenharmony_ci lwsl_err("Done: failed: %d\n", failed); 337d4afb5ceSopenharmony_ci intr = 1; 338d4afb5ceSopenharmony_ci /* 339d4afb5ceSopenharmony_ci * This is how we can exit the event loop even when it's an 340d4afb5ceSopenharmony_ci * event library backing it... it will start and stage the 341d4afb5ceSopenharmony_ci * destroy to happen after we exited this service for each pt 342d4afb5ceSopenharmony_ci */ 343d4afb5ceSopenharmony_ci lws_context_destroy(lws_get_context(wsi)); 344d4afb5ceSopenharmony_ci } 345d4afb5ceSopenharmony_ci 346d4afb5ceSopenharmony_ci return 0; 347d4afb5ceSopenharmony_ci} 348d4afb5ceSopenharmony_ci 349d4afb5ceSopenharmony_cistatic const struct lws_protocols protocols[] = { 350d4afb5ceSopenharmony_ci { "http", callback_http, sizeof(struct pss), 0, 0, NULL, 0 }, 351d4afb5ceSopenharmony_ci LWS_PROTOCOL_LIST_TERM 352d4afb5ceSopenharmony_ci}; 353d4afb5ceSopenharmony_ci 354d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_METRICS) 355d4afb5ceSopenharmony_ci 356d4afb5ceSopenharmony_cistatic int 357d4afb5ceSopenharmony_cimy_metric_report(lws_metric_pub_t *mp) 358d4afb5ceSopenharmony_ci{ 359d4afb5ceSopenharmony_ci lws_metric_bucket_t *sub = mp->u.hist.head; 360d4afb5ceSopenharmony_ci char buf[192]; 361d4afb5ceSopenharmony_ci 362d4afb5ceSopenharmony_ci do { 363d4afb5ceSopenharmony_ci if (lws_metrics_format(mp, &sub, buf, sizeof(buf))) 364d4afb5ceSopenharmony_ci lwsl_user("%s: %s\n", __func__, buf); 365d4afb5ceSopenharmony_ci } while ((mp->flags & LWSMTFL_REPORT_HIST) && sub); 366d4afb5ceSopenharmony_ci 367d4afb5ceSopenharmony_ci /* 0 = leave metric to accumulate, 1 = reset the metric */ 368d4afb5ceSopenharmony_ci 369d4afb5ceSopenharmony_ci return 1; 370d4afb5ceSopenharmony_ci} 371d4afb5ceSopenharmony_ci 372d4afb5ceSopenharmony_cistatic const lws_system_ops_t system_ops = { 373d4afb5ceSopenharmony_ci .metric_report = my_metric_report, 374d4afb5ceSopenharmony_ci}; 375d4afb5ceSopenharmony_ci 376d4afb5ceSopenharmony_ci#endif 377d4afb5ceSopenharmony_ci 378d4afb5ceSopenharmony_cistatic void 379d4afb5ceSopenharmony_cistagger_cb(lws_sorted_usec_list_t *sul); 380d4afb5ceSopenharmony_ci 381d4afb5ceSopenharmony_cistatic void 382d4afb5ceSopenharmony_cilws_try_client_connection(struct lws_client_connect_info *i, int m) 383d4afb5ceSopenharmony_ci{ 384d4afb5ceSopenharmony_ci char path[128]; 385d4afb5ceSopenharmony_ci 386d4afb5ceSopenharmony_ci if (numbered) { 387d4afb5ceSopenharmony_ci lws_snprintf(path, sizeof(path), "/%d.png", m + 1); 388d4afb5ceSopenharmony_ci i->path = path; 389d4afb5ceSopenharmony_ci } else 390d4afb5ceSopenharmony_ci i->path = urlpath; 391d4afb5ceSopenharmony_ci 392d4afb5ceSopenharmony_ci i->pwsi = &client_wsi[m]; 393d4afb5ceSopenharmony_ci i->opaque_user_data = (void *)(intptr_t)m; 394d4afb5ceSopenharmony_ci 395d4afb5ceSopenharmony_ci if (!lws_client_connect_via_info(i)) { 396d4afb5ceSopenharmony_ci failed++; 397d4afb5ceSopenharmony_ci lwsl_user("%s: failed: conn idx %d\n", __func__, m); 398d4afb5ceSopenharmony_ci if (++completed == count) { 399d4afb5ceSopenharmony_ci lwsl_user("Done: failed: %d\n", failed); 400d4afb5ceSopenharmony_ci lws_context_destroy(context); 401d4afb5ceSopenharmony_ci } 402d4afb5ceSopenharmony_ci } else 403d4afb5ceSopenharmony_ci lwsl_user("started connection %s: idx %d (%s)\n", 404d4afb5ceSopenharmony_ci lws_wsi_tag(client_wsi[m]), m, i->path); 405d4afb5ceSopenharmony_ci} 406d4afb5ceSopenharmony_ci 407d4afb5ceSopenharmony_ci 408d4afb5ceSopenharmony_cistatic int 409d4afb5ceSopenharmony_cisystem_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, 410d4afb5ceSopenharmony_ci int current, int target) 411d4afb5ceSopenharmony_ci{ 412d4afb5ceSopenharmony_ci struct lws_context *context = mgr->parent; 413d4afb5ceSopenharmony_ci int m; 414d4afb5ceSopenharmony_ci 415d4afb5ceSopenharmony_ci if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL) 416d4afb5ceSopenharmony_ci return 0; 417d4afb5ceSopenharmony_ci 418d4afb5ceSopenharmony_ci /* all the system prerequisites are ready */ 419d4afb5ceSopenharmony_ci 420d4afb5ceSopenharmony_ci if (!staggered) 421d4afb5ceSopenharmony_ci /* 422d4afb5ceSopenharmony_ci * just pile on all the connections at once, testing the 423d4afb5ceSopenharmony_ci * pipeline queuing before the first is connected 424d4afb5ceSopenharmony_ci */ 425d4afb5ceSopenharmony_ci for (m = 0; m < count; m++) 426d4afb5ceSopenharmony_ci lws_try_client_connection(&i, m); 427d4afb5ceSopenharmony_ci else 428d4afb5ceSopenharmony_ci /* 429d4afb5ceSopenharmony_ci * delay the connections slightly 430d4afb5ceSopenharmony_ci */ 431d4afb5ceSopenharmony_ci lws_sul_schedule(context, 0, &sul_stagger, stagger_cb, 432d4afb5ceSopenharmony_ci 50 * LWS_US_PER_MS); 433d4afb5ceSopenharmony_ci 434d4afb5ceSopenharmony_ci return 0; 435d4afb5ceSopenharmony_ci} 436d4afb5ceSopenharmony_ci 437d4afb5ceSopenharmony_cistatic void 438d4afb5ceSopenharmony_cisignal_cb(void *handle, int signum) 439d4afb5ceSopenharmony_ci{ 440d4afb5ceSopenharmony_ci switch (signum) { 441d4afb5ceSopenharmony_ci case SIGTERM: 442d4afb5ceSopenharmony_ci case SIGINT: 443d4afb5ceSopenharmony_ci break; 444d4afb5ceSopenharmony_ci default: 445d4afb5ceSopenharmony_ci lwsl_err("%s: signal %d\n", __func__, signum); 446d4afb5ceSopenharmony_ci break; 447d4afb5ceSopenharmony_ci } 448d4afb5ceSopenharmony_ci lws_context_destroy(context); 449d4afb5ceSopenharmony_ci} 450d4afb5ceSopenharmony_ci 451d4afb5ceSopenharmony_cistatic void 452d4afb5ceSopenharmony_cisigint_handler(int sig) 453d4afb5ceSopenharmony_ci{ 454d4afb5ceSopenharmony_ci signal_cb(NULL, sig); 455d4afb5ceSopenharmony_ci} 456d4afb5ceSopenharmony_ci 457d4afb5ceSopenharmony_ci#if defined(WIN32) 458d4afb5ceSopenharmony_ciint gettimeofday(struct timeval * tp, struct timezone * tzp) 459d4afb5ceSopenharmony_ci{ 460d4afb5ceSopenharmony_ci // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's 461d4afb5ceSopenharmony_ci // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC) 462d4afb5ceSopenharmony_ci // until 00:00:00 January 1, 1970 463d4afb5ceSopenharmony_ci static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); 464d4afb5ceSopenharmony_ci 465d4afb5ceSopenharmony_ci SYSTEMTIME system_time; 466d4afb5ceSopenharmony_ci FILETIME file_time; 467d4afb5ceSopenharmony_ci uint64_t time; 468d4afb5ceSopenharmony_ci 469d4afb5ceSopenharmony_ci GetSystemTime( &system_time ); 470d4afb5ceSopenharmony_ci SystemTimeToFileTime( &system_time, &file_time ); 471d4afb5ceSopenharmony_ci time = ((uint64_t)file_time.dwLowDateTime ) ; 472d4afb5ceSopenharmony_ci time += ((uint64_t)file_time.dwHighDateTime) << 32; 473d4afb5ceSopenharmony_ci 474d4afb5ceSopenharmony_ci tp->tv_sec = (long) ((time - EPOCH) / 10000000L); 475d4afb5ceSopenharmony_ci tp->tv_usec = (long) (system_time.wMilliseconds * 1000); 476d4afb5ceSopenharmony_ci return 0; 477d4afb5ceSopenharmony_ci} 478d4afb5ceSopenharmony_ci#endif 479d4afb5ceSopenharmony_ci 480d4afb5ceSopenharmony_ciunsigned long long us(void) 481d4afb5ceSopenharmony_ci{ 482d4afb5ceSopenharmony_ci struct timeval t; 483d4afb5ceSopenharmony_ci 484d4afb5ceSopenharmony_ci gettimeofday(&t, NULL); 485d4afb5ceSopenharmony_ci 486d4afb5ceSopenharmony_ci return ((unsigned long long)t.tv_sec * 1000000ull) + (unsigned long long)t.tv_usec; 487d4afb5ceSopenharmony_ci} 488d4afb5ceSopenharmony_ci 489d4afb5ceSopenharmony_cistatic void 490d4afb5ceSopenharmony_cistagger_cb(lws_sorted_usec_list_t *sul) 491d4afb5ceSopenharmony_ci{ 492d4afb5ceSopenharmony_ci lws_usec_t next; 493d4afb5ceSopenharmony_ci 494d4afb5ceSopenharmony_ci /* 495d4afb5ceSopenharmony_ci * open the connections at 100ms intervals, with the 496d4afb5ceSopenharmony_ci * last one being after 1s, testing both queuing, and 497d4afb5ceSopenharmony_ci * direct H2 stream addition stability 498d4afb5ceSopenharmony_ci */ 499d4afb5ceSopenharmony_ci lws_try_client_connection(&i, stagger_idx++); 500d4afb5ceSopenharmony_ci 501d4afb5ceSopenharmony_ci if (stagger_idx == count) 502d4afb5ceSopenharmony_ci return; 503d4afb5ceSopenharmony_ci 504d4afb5ceSopenharmony_ci next = 150 * LWS_US_PER_MS; 505d4afb5ceSopenharmony_ci if (stagger_idx == count - 1) 506d4afb5ceSopenharmony_ci next += 400 * LWS_US_PER_MS; 507d4afb5ceSopenharmony_ci 508d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) 509d4afb5ceSopenharmony_ci if (stagger_idx == 1) 510d4afb5ceSopenharmony_ci next += 600 * LWS_US_PER_MS; 511d4afb5ceSopenharmony_ci#endif 512d4afb5ceSopenharmony_ci 513d4afb5ceSopenharmony_ci lws_sul_schedule(context, 0, &sul_stagger, stagger_cb, next); 514d4afb5ceSopenharmony_ci} 515d4afb5ceSopenharmony_ci 516d4afb5ceSopenharmony_ciint main(int argc, const char **argv) 517d4afb5ceSopenharmony_ci{ 518d4afb5ceSopenharmony_ci lws_state_notify_link_t notifier = { { NULL, NULL, NULL }, 519d4afb5ceSopenharmony_ci system_notify_cb, "app" }; 520d4afb5ceSopenharmony_ci lws_state_notify_link_t *na[] = { ¬ifier, NULL }; 521d4afb5ceSopenharmony_ci struct lws_context_creation_info info; 522d4afb5ceSopenharmony_ci unsigned long long start; 523d4afb5ceSopenharmony_ci const char *p; 524d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) 525d4afb5ceSopenharmony_ci int pl = 0; 526d4afb5ceSopenharmony_ci#endif 527d4afb5ceSopenharmony_ci 528d4afb5ceSopenharmony_ci memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 529d4afb5ceSopenharmony_ci memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ 530d4afb5ceSopenharmony_ci 531d4afb5ceSopenharmony_ci lws_cmdline_option_handle_builtin(argc, argv, &info); 532d4afb5ceSopenharmony_ci 533d4afb5ceSopenharmony_ci info.signal_cb = signal_cb; 534d4afb5ceSopenharmony_ci info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 535d4afb5ceSopenharmony_ci 536d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--uv")) 537d4afb5ceSopenharmony_ci info.options |= LWS_SERVER_OPTION_LIBUV; 538d4afb5ceSopenharmony_ci else 539d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--event")) 540d4afb5ceSopenharmony_ci info.options |= LWS_SERVER_OPTION_LIBEVENT; 541d4afb5ceSopenharmony_ci else 542d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--ev")) 543d4afb5ceSopenharmony_ci info.options |= LWS_SERVER_OPTION_LIBEV; 544d4afb5ceSopenharmony_ci else 545d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--glib")) 546d4afb5ceSopenharmony_ci info.options |= LWS_SERVER_OPTION_GLIB; 547d4afb5ceSopenharmony_ci else 548d4afb5ceSopenharmony_ci signal(SIGINT, sigint_handler); 549d4afb5ceSopenharmony_ci 550d4afb5ceSopenharmony_ci staggered = !!lws_cmdline_option(argc, argv, "-s"); 551d4afb5ceSopenharmony_ci 552d4afb5ceSopenharmony_ci lwsl_user("LWS minimal http client [-s (staggered)] [-p (pipeline)]\n"); 553d4afb5ceSopenharmony_ci lwsl_user(" [--h1 (http/1 only)] [-l (localhost)] [-d <logs>]\n"); 554d4afb5ceSopenharmony_ci lwsl_user(" [-n (numbered)] [--post]\n"); 555d4afb5ceSopenharmony_ci 556d4afb5ceSopenharmony_ci info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ 557d4afb5ceSopenharmony_ci info.protocols = protocols; 558d4afb5ceSopenharmony_ci /* 559d4afb5ceSopenharmony_ci * since we know this lws context is only ever going to be used with 560d4afb5ceSopenharmony_ci * COUNT client wsis / fds / sockets at a time, let lws know it doesn't 561d4afb5ceSopenharmony_ci * have to use the default allocations for fd tables up to ulimit -n. 562d4afb5ceSopenharmony_ci * It will just allocate for 1 internal and COUNT + 1 (allowing for h2 563d4afb5ceSopenharmony_ci * network wsi) that we will use. 564d4afb5ceSopenharmony_ci */ 565d4afb5ceSopenharmony_ci info.fd_limit_per_thread = 1 + COUNT + 1; 566d4afb5ceSopenharmony_ci info.register_notifier_list = na; 567d4afb5ceSopenharmony_ci info.pcontext = &context; 568d4afb5ceSopenharmony_ci 569d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SYS_METRICS) 570d4afb5ceSopenharmony_ci info.system_ops = &system_ops; 571d4afb5ceSopenharmony_ci#endif 572d4afb5ceSopenharmony_ci 573d4afb5ceSopenharmony_ci#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL) 574d4afb5ceSopenharmony_ci /* 575d4afb5ceSopenharmony_ci * OpenSSL uses the system trust store. mbedTLS has to be told which 576d4afb5ceSopenharmony_ci * CA to trust explicitly. 577d4afb5ceSopenharmony_ci */ 578d4afb5ceSopenharmony_ci info.client_ssl_ca_filepath = "./warmcat.com.cer"; 579d4afb5ceSopenharmony_ci#endif 580d4afb5ceSopenharmony_ci 581d4afb5ceSopenharmony_ci /* vhost option allowing tls session reuse, requires 582d4afb5ceSopenharmony_ci * LWS_WITH_TLS_SESSIONS build option */ 583d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--no-tls-session-reuse")) 584d4afb5ceSopenharmony_ci info.options |= LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE; 585d4afb5ceSopenharmony_ci 586d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--limit"))) 587d4afb5ceSopenharmony_ci info.simultaneous_ssl_restriction = atoi(p); 588d4afb5ceSopenharmony_ci 589d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--ssl-handshake-serialize"))) 590d4afb5ceSopenharmony_ci /* We only consider simultaneous_ssl_restriction > 1 use cases. 591d4afb5ceSopenharmony_ci * If ssl isn't limited or only 1 is allowed, we don't care. 592d4afb5ceSopenharmony_ci */ 593d4afb5ceSopenharmony_ci info.simultaneous_ssl_handshake_restriction = atoi(p); 594d4afb5ceSopenharmony_ci 595d4afb5ceSopenharmony_ci context = lws_create_context(&info); 596d4afb5ceSopenharmony_ci if (!context) { 597d4afb5ceSopenharmony_ci lwsl_err("lws init failed\n"); 598d4afb5ceSopenharmony_ci return 1; 599d4afb5ceSopenharmony_ci } 600d4afb5ceSopenharmony_ci 601d4afb5ceSopenharmony_ci#if defined(LWS_ROLE_H2) && defined(LWS_ROLE_H1) 602d4afb5ceSopenharmony_ci i.alpn = "h2,http/1.1"; 603d4afb5ceSopenharmony_ci#elif defined(LWS_ROLE_H2) 604d4afb5ceSopenharmony_ci i.alpn = "h2"; 605d4afb5ceSopenharmony_ci#elif defined(LWS_ROLE_H1) 606d4afb5ceSopenharmony_ci i.alpn = "http/1.1"; 607d4afb5ceSopenharmony_ci#endif 608d4afb5ceSopenharmony_ci 609d4afb5ceSopenharmony_ci i.context = context; 610d4afb5ceSopenharmony_ci i.ssl_connection = LCCSCF_USE_SSL | 611d4afb5ceSopenharmony_ci LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | 612d4afb5ceSopenharmony_ci LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; 613d4afb5ceSopenharmony_ci 614d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--post")) { 615d4afb5ceSopenharmony_ci posting = 1; 616d4afb5ceSopenharmony_ci i.method = "POST"; 617d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_HTTP_MULTIPART_MIME; 618d4afb5ceSopenharmony_ci } else 619d4afb5ceSopenharmony_ci i.method = "GET"; 620d4afb5ceSopenharmony_ci 621d4afb5ceSopenharmony_ci /* enables h1 or h2 connection sharing */ 622d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "-p")) { 623d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_PIPELINE; 624d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) 625d4afb5ceSopenharmony_ci pl = 1; 626d4afb5ceSopenharmony_ci#endif 627d4afb5ceSopenharmony_ci } 628d4afb5ceSopenharmony_ci 629d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CONMON) 630d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--conmon")) 631d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_CONMON; 632d4afb5ceSopenharmony_ci#endif 633d4afb5ceSopenharmony_ci 634d4afb5ceSopenharmony_ci /* force h1 even if h2 available */ 635d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--h1")) 636d4afb5ceSopenharmony_ci i.alpn = "http/1.1"; 637d4afb5ceSopenharmony_ci 638d4afb5ceSopenharmony_ci strcpy(urlpath, "/"); 639d4afb5ceSopenharmony_ci 640d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "-l")) { 641d4afb5ceSopenharmony_ci i.port = 7681; 642d4afb5ceSopenharmony_ci i.address = "localhost"; 643d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; 644d4afb5ceSopenharmony_ci if (posting) 645d4afb5ceSopenharmony_ci strcpy(urlpath, "/formtest"); 646d4afb5ceSopenharmony_ci } else { 647d4afb5ceSopenharmony_ci i.port = 443; 648d4afb5ceSopenharmony_ci i.address = "libwebsockets.org"; 649d4afb5ceSopenharmony_ci if (posting) 650d4afb5ceSopenharmony_ci strcpy(urlpath, "/testserver/formtest"); 651d4afb5ceSopenharmony_ci } 652d4afb5ceSopenharmony_ci 653d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--no-tls")) 654d4afb5ceSopenharmony_ci i.ssl_connection &= ~(LCCSCF_USE_SSL); 655d4afb5ceSopenharmony_ci 656d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "-n")) 657d4afb5ceSopenharmony_ci numbered = 1; 658d4afb5ceSopenharmony_ci 659d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--server"))) 660d4afb5ceSopenharmony_ci i.address = p; 661d4afb5ceSopenharmony_ci 662d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--port"))) 663d4afb5ceSopenharmony_ci i.port = atoi(p); 664d4afb5ceSopenharmony_ci 665d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--path"))) 666d4afb5ceSopenharmony_ci lws_strncpy(urlpath, p, sizeof(urlpath)); 667d4afb5ceSopenharmony_ci 668d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-c"))) 669d4afb5ceSopenharmony_ci if (atoi(p) <= COUNT && atoi(p)) 670d4afb5ceSopenharmony_ci count = atoi(p); 671d4afb5ceSopenharmony_ci 672d4afb5ceSopenharmony_ci i.host = i.address; 673d4afb5ceSopenharmony_ci i.origin = i.address; 674d4afb5ceSopenharmony_ci i.protocol = protocols[0].name; 675d4afb5ceSopenharmony_ci 676d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) && !defined(LWS_WITH_MBEDTLS) && !defined(WIN32) 677d4afb5ceSopenharmony_ci /* 678d4afb5ceSopenharmony_ci * Attempt to preload a session from external storage 679d4afb5ceSopenharmony_ci */ 680d4afb5ceSopenharmony_ci if (lws_tls_session_dump_load(lws_get_vhost_by_name(context, "default"), 681d4afb5ceSopenharmony_ci i.host, (uint16_t)i.port, sess_load_cb, "/tmp")) 682d4afb5ceSopenharmony_ci lwsl_warn("%s: session load failed\n", __func__); 683d4afb5ceSopenharmony_ci#endif 684d4afb5ceSopenharmony_ci 685d4afb5ceSopenharmony_ci start = us(); 686d4afb5ceSopenharmony_ci while (!intr && !lws_service(context, 0)) 687d4afb5ceSopenharmony_ci ; 688d4afb5ceSopenharmony_ci 689d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS_SESSIONS) 690d4afb5ceSopenharmony_ci lwsl_user("%s: session reuse count %d\n", __func__, reuse); 691d4afb5ceSopenharmony_ci 692d4afb5ceSopenharmony_ci if (staggered && !pl && !reuse) { 693d4afb5ceSopenharmony_ci lwsl_err("%s: failing, expected 1 .. %d reused\n", __func__, count - 1); 694d4afb5ceSopenharmony_ci // too difficult to reproduce in CI 695d4afb5ceSopenharmony_ci // failed = 1; 696d4afb5ceSopenharmony_ci } 697d4afb5ceSopenharmony_ci#endif 698d4afb5ceSopenharmony_ci 699d4afb5ceSopenharmony_ci lwsl_user("Duration: %lldms\n", (us() - start) / 1000); 700d4afb5ceSopenharmony_ci lws_context_destroy(context); 701d4afb5ceSopenharmony_ci 702d4afb5ceSopenharmony_ci lwsl_user("Exiting with %d\n", failed || completed != count); 703d4afb5ceSopenharmony_ci 704d4afb5ceSopenharmony_ci return failed || completed != count; 705d4afb5ceSopenharmony_ci} 706