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[] = { &notifier, 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