1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets-test-server - libwebsockets test implementation
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 * The person who associated a work with this deed has dedicated
10d4afb5ceSopenharmony_ci * the work to the public domain by waiving all of his or her rights
11d4afb5ceSopenharmony_ci * to the work worldwide under copyright law, including all related
12d4afb5ceSopenharmony_ci * and neighboring rights, to the extent allowed by law. You can copy,
13d4afb5ceSopenharmony_ci * modify, distribute and perform the work, even for commercial purposes,
14d4afb5ceSopenharmony_ci * all without asking permission.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * The test apps are intended to be adapted for use in your code, which
17d4afb5ceSopenharmony_ci * may be proprietary.  So unlike the library itself, they are licensed
18d4afb5ceSopenharmony_ci * Public Domain.
19d4afb5ceSopenharmony_ci *
20d4afb5ceSopenharmony_ci * Scrapeable, proxiable OpenMetrics metrics (compatible with Prometheus)
21d4afb5ceSopenharmony_ci *
22d4afb5ceSopenharmony_ci * https://tools.ietf.org/html/draft-richih-opsawg-openmetrics-00
23d4afb5ceSopenharmony_ci *
24d4afb5ceSopenharmony_ci * This plugin provides four protocols related to openmetrics handling:
25d4afb5ceSopenharmony_ci *
26d4afb5ceSopenharmony_ci * 1) "lws-openmetrics" direct http listener so scraper can directly get metrics
27d4afb5ceSopenharmony_ci *
28d4afb5ceSopenharmony_ci * 2) "lws-openmetrics-prox-agg" metrics proxy server that scraper can connect
29d4afb5ceSopenharmony_ci *    to locally to proxy through to connected remote clients at 3)
30d4afb5ceSopenharmony_ci *
31d4afb5ceSopenharmony_ci * 3) "lws-openmetrics-prox-server" metrics proxy server that remote clients can
32d4afb5ceSopenharmony_ci *    connect to, providing a path where scrapers at 2) can get metrics from
33d4afb5ceSopenharmony_ci *    clients connected us
34d4afb5ceSopenharmony_ci *
35d4afb5ceSopenharmony_ci * 4) "lws-openmetrics-prox-client" nailed-up metrics proxy client that tries to
36d4afb5ceSopenharmony_ci *    keep up a connection to the server at 3), allowing to scraper to reach
37d4afb5ceSopenharmony_ci *    clients that have no reachable way to serve.
38d4afb5ceSopenharmony_ci *
39d4afb5ceSopenharmony_ci * These are provided like this to maximize flexibility in being able to add
40d4afb5ceSopenharmony_ci * openmetrics serving, proxying, or client->proxy to existing lws code.
41d4afb5ceSopenharmony_ci *
42d4afb5ceSopenharmony_ci * Openmetrics supports a "metric" at the top of its report that describes the
43d4afb5ceSopenharmony_ci * source aka "target metadata".
44d4afb5ceSopenharmony_ci *
45d4afb5ceSopenharmony_ci * Since we want to enable collection from devices that are not externally
46d4afb5ceSopenharmony_ci * reachable, we must provide a reachable server that the clients can attach to
47d4afb5ceSopenharmony_ci * and have their stats aggregated and then read by Prometheus or whatever.
48d4afb5ceSopenharmony_ci * Openmetrics says that it wants to present the aggregated stats in a flat
49d4afb5ceSopenharmony_ci * summary with only the aggregator's "target metadata" and contributor targets
50d4afb5ceSopenharmony_ci * getting their data tagged with the source
51d4afb5ceSopenharmony_ci *
52d4afb5ceSopenharmony_ci * "The above discussion is in the context of individual exposers.  An
53d4afb5ceSopenharmony_ci *  exposition from a general purpose monitoring system may contain
54d4afb5ceSopenharmony_ci *  metrics from many individual targets, and thus may expose multiple
55d4afb5ceSopenharmony_ci *  target info Metrics.  The metrics may already have had target
56d4afb5ceSopenharmony_ci *  metadata added to them as labels as part of ingestion.  The metric
57d4afb5ceSopenharmony_ci *  names MUST NOT be varied based on target metadata.  For example it
58d4afb5ceSopenharmony_ci *  would be incorrect for all metrics to end up being prefixed with
59d4afb5ceSopenharmony_ci *  staging_ even if they all originated from targets in a staging
60d4afb5ceSopenharmony_ci *  environment)."
61d4afb5ceSopenharmony_ci */
62d4afb5ceSopenharmony_ci
63d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC)
64d4afb5ceSopenharmony_ci#if !defined(LWS_DLL)
65d4afb5ceSopenharmony_ci#define LWS_DLL
66d4afb5ceSopenharmony_ci#endif
67d4afb5ceSopenharmony_ci#if !defined(LWS_INTERNAL)
68d4afb5ceSopenharmony_ci#define LWS_INTERNAL
69d4afb5ceSopenharmony_ci#endif
70d4afb5ceSopenharmony_ci#include <libwebsockets.h>
71d4afb5ceSopenharmony_ci#endif
72d4afb5ceSopenharmony_ci#include <string.h>
73d4afb5ceSopenharmony_ci#include <stdlib.h>
74d4afb5ceSopenharmony_ci#include <sys/stat.h>
75d4afb5ceSopenharmony_ci#include <fcntl.h>
76d4afb5ceSopenharmony_ci#if !defined(WIN32)
77d4afb5ceSopenharmony_ci#include <unistd.h>
78d4afb5ceSopenharmony_ci#endif
79d4afb5ceSopenharmony_ci#include <assert.h>
80d4afb5ceSopenharmony_ci
81d4afb5ceSopenharmony_cistruct vhd {
82d4afb5ceSopenharmony_ci	struct lws_context	*cx;
83d4afb5ceSopenharmony_ci	struct lws_vhost	*vhost;
84d4afb5ceSopenharmony_ci
85d4afb5ceSopenharmony_ci	char			ws_server_uri[128];
86d4afb5ceSopenharmony_ci	char			metrics_proxy_path[128];
87d4afb5ceSopenharmony_ci	char			ba_secret[128];
88d4afb5ceSopenharmony_ci
89d4afb5ceSopenharmony_ci	const char		*proxy_side_bind_name;
90d4afb5ceSopenharmony_ci	/**< name used to bind the two halves of the proxy together, must be
91d4afb5ceSopenharmony_ci	 * the same name given in a pvo for both "lws-openmetrics-prox-agg"
92d4afb5ceSopenharmony_ci	 * (the side local to the scraper) and "lws-openmetrics-prox-server"
93d4afb5ceSopenharmony_ci	 * (the side the clients connect to)
94d4afb5ceSopenharmony_ci	 */
95d4afb5ceSopenharmony_ci
96d4afb5ceSopenharmony_ci	char			sanity[8];
97d4afb5ceSopenharmony_ci
98d4afb5ceSopenharmony_ci	lws_dll2_owner_t	clients;
99d4afb5ceSopenharmony_ci
100d4afb5ceSopenharmony_ci	lws_sorted_usec_list_t	sul;	     /* schedule connection retry */
101d4afb5ceSopenharmony_ci
102d4afb5ceSopenharmony_ci	struct vhd		*bind_partner_vhd;
103d4afb5ceSopenharmony_ci
104d4afb5ceSopenharmony_ci	struct lws		*wsi;	     /* related wsi if any */
105d4afb5ceSopenharmony_ci	uint16_t		retry_count; /* count of consequetive retries */
106d4afb5ceSopenharmony_ci};
107d4afb5ceSopenharmony_ci
108d4afb5ceSopenharmony_cistruct pss {
109d4afb5ceSopenharmony_ci	lws_dll2_t		list;
110d4afb5ceSopenharmony_ci	char			proxy_path[64];
111d4afb5ceSopenharmony_ci	struct lwsac		*ac;	/* the translated metrics, one ac per line */
112d4afb5ceSopenharmony_ci	struct lwsac		*walk;	/* iterator for ac when writing */
113d4afb5ceSopenharmony_ci	size_t			tot;	/* content-length computation */
114d4afb5ceSopenharmony_ci	struct lws		*wsi;
115d4afb5ceSopenharmony_ci
116d4afb5ceSopenharmony_ci	uint8_t			greet:1; /* set if client needs to send proxy path */
117d4afb5ceSopenharmony_ci	uint8_t			trigger:1; /* we want to ask the client to dump */
118d4afb5ceSopenharmony_ci};
119d4afb5ceSopenharmony_ci
120d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CLIENT)
121d4afb5ceSopenharmony_cistatic const uint32_t backoff_ms[] = { 1000, 2000, 3000, 4000, 5000 };
122d4afb5ceSopenharmony_ci
123d4afb5ceSopenharmony_cistatic const lws_retry_bo_t retry = {
124d4afb5ceSopenharmony_ci	.retry_ms_table			= backoff_ms,
125d4afb5ceSopenharmony_ci	.retry_ms_table_count		= LWS_ARRAY_SIZE(backoff_ms),
126d4afb5ceSopenharmony_ci	.conceal_count			= LWS_ARRAY_SIZE(backoff_ms),
127d4afb5ceSopenharmony_ci
128d4afb5ceSopenharmony_ci	.secs_since_valid_ping		= 400,  /* force PINGs after secs idle */
129d4afb5ceSopenharmony_ci	.secs_since_valid_hangup	= 400, /* hangup after secs idle */
130d4afb5ceSopenharmony_ci
131d4afb5ceSopenharmony_ci	.jitter_percent			= 0,
132d4afb5ceSopenharmony_ci};
133d4afb5ceSopenharmony_ci
134d4afb5ceSopenharmony_cistatic void
135d4afb5ceSopenharmony_ciomc_connect_client(lws_sorted_usec_list_t *sul)
136d4afb5ceSopenharmony_ci{
137d4afb5ceSopenharmony_ci	struct vhd *vhd = lws_container_of(sul, struct vhd, sul);
138d4afb5ceSopenharmony_ci	struct lws_client_connect_info i;
139d4afb5ceSopenharmony_ci	const char *prot;
140d4afb5ceSopenharmony_ci	char url[128];
141d4afb5ceSopenharmony_ci
142d4afb5ceSopenharmony_ci	memset(&i, 0, sizeof(i));
143d4afb5ceSopenharmony_ci
144d4afb5ceSopenharmony_ci	lwsl_notice("%s: %s %s %s\n", __func__, vhd->ws_server_uri, vhd->metrics_proxy_path, vhd->ba_secret);
145d4afb5ceSopenharmony_ci
146d4afb5ceSopenharmony_ci	lws_strncpy(url, vhd->ws_server_uri, sizeof(url));
147d4afb5ceSopenharmony_ci
148d4afb5ceSopenharmony_ci	if (lws_parse_uri(url, &prot, &i.address, &i.port, &i.path)) {
149d4afb5ceSopenharmony_ci		lwsl_err("%s: unable to parse uri %s\n", __func__,
150d4afb5ceSopenharmony_ci			 vhd->ws_server_uri);
151d4afb5ceSopenharmony_ci		return;
152d4afb5ceSopenharmony_ci	}
153d4afb5ceSopenharmony_ci
154d4afb5ceSopenharmony_ci	i.context		= vhd->cx;
155d4afb5ceSopenharmony_ci	i.origin		= i.address;
156d4afb5ceSopenharmony_ci	i.host			= i.address;
157d4afb5ceSopenharmony_ci	i.ssl_connection	= LCCSCF_USE_SSL;
158d4afb5ceSopenharmony_ci	i.protocol		= "lws-openmetrics-prox-server"; /* public subprot */
159d4afb5ceSopenharmony_ci	i.local_protocol_name	= "lws-openmetrics-prox-client";
160d4afb5ceSopenharmony_ci	i.pwsi			= &vhd->wsi;
161d4afb5ceSopenharmony_ci	i.retry_and_idle_policy = &retry;
162d4afb5ceSopenharmony_ci	i.userdata		= vhd;
163d4afb5ceSopenharmony_ci	i.vhost			= vhd->vhost;
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ci	lwsl_notice("%s: %s %u %s\n", __func__, i.address, i.port, i.path);
166d4afb5ceSopenharmony_ci
167d4afb5ceSopenharmony_ci	if (lws_client_connect_via_info(&i))
168d4afb5ceSopenharmony_ci		return;
169d4afb5ceSopenharmony_ci
170d4afb5ceSopenharmony_ci	/*
171d4afb5ceSopenharmony_ci	 * Failed... schedule a retry... we can't use the _retry_wsi()
172d4afb5ceSopenharmony_ci	 * convenience wrapper api here because no valid wsi at this
173d4afb5ceSopenharmony_ci	 * point.
174d4afb5ceSopenharmony_ci	 */
175d4afb5ceSopenharmony_ci	if (!lws_retry_sul_schedule(vhd->cx, 0, sul, &retry,
176d4afb5ceSopenharmony_ci				    omc_connect_client, &vhd->retry_count))
177d4afb5ceSopenharmony_ci		return;
178d4afb5ceSopenharmony_ci
179d4afb5ceSopenharmony_ci	vhd->retry_count = 0;
180d4afb5ceSopenharmony_ci	lws_retry_sul_schedule(vhd->cx, 0, sul, &retry,
181d4afb5ceSopenharmony_ci			       omc_connect_client, &vhd->retry_count);
182d4afb5ceSopenharmony_ci}
183d4afb5ceSopenharmony_ci#endif
184d4afb5ceSopenharmony_ci
185d4afb5ceSopenharmony_cistatic void
186d4afb5ceSopenharmony_ciopenmetrics_san(char *nm, size_t nl)
187d4afb5ceSopenharmony_ci{
188d4afb5ceSopenharmony_ci	size_t m;
189d4afb5ceSopenharmony_ci
190d4afb5ceSopenharmony_ci	/* Openmetrics has a very restricted token charset */
191d4afb5ceSopenharmony_ci
192d4afb5ceSopenharmony_ci	for (m = 0; m < nl; m++)
193d4afb5ceSopenharmony_ci		if ((nm[m] < 'A' || nm[m] > 'Z') &&
194d4afb5ceSopenharmony_ci		    (nm[m] < 'a' || nm[m] > 'z') &&
195d4afb5ceSopenharmony_ci		    (nm[m] < '0' || nm[m] > '9') &&
196d4afb5ceSopenharmony_ci		    nm[m] != '_')
197d4afb5ceSopenharmony_ci			nm[m] = '_';
198d4afb5ceSopenharmony_ci}
199d4afb5ceSopenharmony_ci
200d4afb5ceSopenharmony_cistatic int
201d4afb5ceSopenharmony_cilws_metrics_om_format_agg(lws_metric_pub_t *pub, const char *nm, lws_usec_t now,
202d4afb5ceSopenharmony_ci			  int gng, char *buf, size_t len)
203d4afb5ceSopenharmony_ci{
204d4afb5ceSopenharmony_ci	const char *_gng = gng ? "_nogo" : "_go";
205d4afb5ceSopenharmony_ci	char *end = buf + len - 1, *obuf = buf;
206d4afb5ceSopenharmony_ci
207d4afb5ceSopenharmony_ci	if (pub->flags & LWSMTFL_REPORT_ONLY_GO)
208d4afb5ceSopenharmony_ci		_gng = "";
209d4afb5ceSopenharmony_ci
210d4afb5ceSopenharmony_ci	if (!(pub->flags & LWSMTFL_REPORT_MEAN)) {
211d4afb5ceSopenharmony_ci		/* only the sum is meaningful */
212d4afb5ceSopenharmony_ci		if (pub->flags & LWSMTFL_REPORT_DUTY_WALLCLOCK_US) {
213d4afb5ceSopenharmony_ci			buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
214d4afb5ceSopenharmony_ci				"%s_count %u\n"
215d4afb5ceSopenharmony_ci				"%s_us_sum %llu\n"
216d4afb5ceSopenharmony_ci				"%s_created %lu.%06u\n",
217d4afb5ceSopenharmony_ci				nm, (unsigned int)pub->u.agg.count[gng],
218d4afb5ceSopenharmony_ci				nm, (unsigned long long)pub->u.agg.sum[gng],
219d4afb5ceSopenharmony_ci				nm, (unsigned long)(pub->us_first / 1000000),
220d4afb5ceSopenharmony_ci				    (unsigned int)(pub->us_first % 1000000));
221d4afb5ceSopenharmony_ci
222d4afb5ceSopenharmony_ci			return lws_ptr_diff(buf, obuf);
223d4afb5ceSopenharmony_ci		}
224d4afb5ceSopenharmony_ci
225d4afb5ceSopenharmony_ci		/* it's a monotonic ordinal, like total tx */
226d4afb5ceSopenharmony_ci		buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
227d4afb5ceSopenharmony_ci				    "%s%s_count %u\n"
228d4afb5ceSopenharmony_ci				    "%s%s_sum %llu\n",
229d4afb5ceSopenharmony_ci				    nm, _gng,
230d4afb5ceSopenharmony_ci				    (unsigned int)pub->u.agg.count[gng],
231d4afb5ceSopenharmony_ci				    nm, _gng,
232d4afb5ceSopenharmony_ci				    (unsigned long long)pub->u.agg.sum[gng]);
233d4afb5ceSopenharmony_ci
234d4afb5ceSopenharmony_ci	} else
235d4afb5ceSopenharmony_ci		buf += lws_snprintf(buf, lws_ptr_diff_size_t(end, buf),
236d4afb5ceSopenharmony_ci				    "%s%s_count %u\n"
237d4afb5ceSopenharmony_ci				    "%s%s_mean %llu\n",
238d4afb5ceSopenharmony_ci				    nm, _gng,
239d4afb5ceSopenharmony_ci				    (unsigned int)pub->u.agg.count[gng],
240d4afb5ceSopenharmony_ci				    nm, _gng, (unsigned long long)
241d4afb5ceSopenharmony_ci				    (pub->u.agg.count[gng] ?
242d4afb5ceSopenharmony_ci						pub->u.agg.sum[gng] /
243d4afb5ceSopenharmony_ci						pub->u.agg.count[gng] : 0));
244d4afb5ceSopenharmony_ci
245d4afb5ceSopenharmony_ci	return lws_ptr_diff(buf, obuf);
246d4afb5ceSopenharmony_ci}
247d4afb5ceSopenharmony_ci
248d4afb5ceSopenharmony_cistatic int
249d4afb5ceSopenharmony_cilws_metrics_om_ac_stash(struct pss *pss, const char *buf, size_t len)
250d4afb5ceSopenharmony_ci{
251d4afb5ceSopenharmony_ci	char *q;
252d4afb5ceSopenharmony_ci
253d4afb5ceSopenharmony_ci	q = lwsac_use(&pss->ac, LWS_PRE + len + 2, LWS_PRE + len + 2);
254d4afb5ceSopenharmony_ci	if (!q) {
255d4afb5ceSopenharmony_ci		lwsac_free(&pss->ac);
256d4afb5ceSopenharmony_ci
257d4afb5ceSopenharmony_ci		return -1;
258d4afb5ceSopenharmony_ci	}
259d4afb5ceSopenharmony_ci	q[LWS_PRE] = (char)((len >> 8) & 0xff);
260d4afb5ceSopenharmony_ci	q[LWS_PRE + 1] = (char)(len & 0xff);
261d4afb5ceSopenharmony_ci	memcpy(q + LWS_PRE + 2, buf, len);
262d4afb5ceSopenharmony_ci	pss->tot += len;
263d4afb5ceSopenharmony_ci
264d4afb5ceSopenharmony_ci	return 0;
265d4afb5ceSopenharmony_ci}
266d4afb5ceSopenharmony_ci
267d4afb5ceSopenharmony_ci/*
268d4afb5ceSopenharmony_ci * We have to do the ac listing at this level, because there can be too large
269d4afb5ceSopenharmony_ci * a number to metrics tags to iterate that can fit in a reasonable buffer.
270d4afb5ceSopenharmony_ci */
271d4afb5ceSopenharmony_ci
272d4afb5ceSopenharmony_cistatic int
273d4afb5ceSopenharmony_cilws_metrics_om_format(struct pss *pss, lws_metric_pub_t *pub, const char *nm)
274d4afb5ceSopenharmony_ci{
275d4afb5ceSopenharmony_ci	char buf[1200], *p = buf, *end = buf + sizeof(buf) - 1, tmp[512];
276d4afb5ceSopenharmony_ci	lws_usec_t t = lws_now_usecs();
277d4afb5ceSopenharmony_ci
278d4afb5ceSopenharmony_ci	if (pub->flags & LWSMTFL_REPORT_HIST) {
279d4afb5ceSopenharmony_ci		lws_metric_bucket_t *buck = pub->u.hist.head;
280d4afb5ceSopenharmony_ci
281d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
282d4afb5ceSopenharmony_ci				  "%s_count %llu\n",
283d4afb5ceSopenharmony_ci				  nm, (unsigned long long)
284d4afb5ceSopenharmony_ci				  pub->u.hist.total_count);
285d4afb5ceSopenharmony_ci
286d4afb5ceSopenharmony_ci		while (buck) {
287d4afb5ceSopenharmony_ci			lws_strncpy(tmp, lws_metric_bucket_name(buck),
288d4afb5ceSopenharmony_ci				    sizeof(tmp));
289d4afb5ceSopenharmony_ci
290d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
291d4afb5ceSopenharmony_ci					  "%s{%s} %llu\n", nm, tmp,
292d4afb5ceSopenharmony_ci					  (unsigned long long)buck->count);
293d4afb5ceSopenharmony_ci
294d4afb5ceSopenharmony_ci			lws_metrics_om_ac_stash(pss, buf,
295d4afb5ceSopenharmony_ci						lws_ptr_diff_size_t(p, buf));
296d4afb5ceSopenharmony_ci			p = buf;
297d4afb5ceSopenharmony_ci
298d4afb5ceSopenharmony_ci			buck = buck->next;
299d4afb5ceSopenharmony_ci		}
300d4afb5ceSopenharmony_ci
301d4afb5ceSopenharmony_ci		goto happy;
302d4afb5ceSopenharmony_ci	}
303d4afb5ceSopenharmony_ci
304d4afb5ceSopenharmony_ci	if (!pub->u.agg.count[METRES_GO] && !pub->u.agg.count[METRES_NOGO])
305d4afb5ceSopenharmony_ci		return 0;
306d4afb5ceSopenharmony_ci
307d4afb5ceSopenharmony_ci	if (pub->u.agg.count[METRES_GO])
308d4afb5ceSopenharmony_ci		p += lws_metrics_om_format_agg(pub, nm, t, METRES_GO, p,
309d4afb5ceSopenharmony_ci					       lws_ptr_diff_size_t(end, p));
310d4afb5ceSopenharmony_ci
311d4afb5ceSopenharmony_ci	if (!(pub->flags & LWSMTFL_REPORT_ONLY_GO) &&
312d4afb5ceSopenharmony_ci	    pub->u.agg.count[METRES_NOGO])
313d4afb5ceSopenharmony_ci		p += lws_metrics_om_format_agg(pub, nm, t, METRES_NOGO, p,
314d4afb5ceSopenharmony_ci					       lws_ptr_diff_size_t(end, p));
315d4afb5ceSopenharmony_ci
316d4afb5ceSopenharmony_ci	if (pub->flags & LWSMTFL_REPORT_MEAN)
317d4afb5ceSopenharmony_ci		p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
318d4afb5ceSopenharmony_ci				  "%s_min %llu\n"
319d4afb5ceSopenharmony_ci				  "%s_max %llu\n",
320d4afb5ceSopenharmony_ci				  nm, (unsigned long long)pub->u.agg.min,
321d4afb5ceSopenharmony_ci				  nm, (unsigned long long)pub->u.agg.max);
322d4afb5ceSopenharmony_ci
323d4afb5ceSopenharmony_cihappy:
324d4afb5ceSopenharmony_ci	return lws_metrics_om_ac_stash(pss, buf, lws_ptr_diff_size_t(p, buf));
325d4afb5ceSopenharmony_ci}
326d4afb5ceSopenharmony_ci
327d4afb5ceSopenharmony_cistatic int
328d4afb5ceSopenharmony_ciappend_om_metric(lws_metric_pub_t *pub, void *user)
329d4afb5ceSopenharmony_ci{
330d4afb5ceSopenharmony_ci	struct pss *pss = (struct pss *)user;
331d4afb5ceSopenharmony_ci	char nm[64];
332d4afb5ceSopenharmony_ci	size_t nl;
333d4afb5ceSopenharmony_ci
334d4afb5ceSopenharmony_ci	/*
335d4afb5ceSopenharmony_ci	 * Convert lws_metrics to openmetrics metrics data, stashing into an
336d4afb5ceSopenharmony_ci	 * lwsac without backfill.  Since it's not backfilling, use areas are in
337d4afb5ceSopenharmony_ci	 * linear sequence simplifying walking them.  Limiting the lwsac alloc
338d4afb5ceSopenharmony_ci	 * to less than a typical mtu means we can write one per write
339d4afb5ceSopenharmony_ci	 * efficiently
340d4afb5ceSopenharmony_ci	 */
341d4afb5ceSopenharmony_ci
342d4afb5ceSopenharmony_ci	lws_strncpy(nm, pub->name, sizeof(nm));
343d4afb5ceSopenharmony_ci	nl = strlen(nm);
344d4afb5ceSopenharmony_ci
345d4afb5ceSopenharmony_ci	openmetrics_san(nm, nl);
346d4afb5ceSopenharmony_ci
347d4afb5ceSopenharmony_ci	return lws_metrics_om_format(pss, pub, nm);
348d4afb5ceSopenharmony_ci}
349d4afb5ceSopenharmony_ci
350d4afb5ceSopenharmony_ci#if defined(__linux__)
351d4afb5ceSopenharmony_cistatic int
352d4afb5ceSopenharmony_cigrabfile(const char *fi, char *buf, size_t len)
353d4afb5ceSopenharmony_ci{
354d4afb5ceSopenharmony_ci	int n, fd = lws_open(fi, LWS_O_RDONLY);
355d4afb5ceSopenharmony_ci
356d4afb5ceSopenharmony_ci	buf[0] = '\0';
357d4afb5ceSopenharmony_ci	if (fd < 0)
358d4afb5ceSopenharmony_ci		return -1;
359d4afb5ceSopenharmony_ci
360d4afb5ceSopenharmony_ci	n = (int)read(fd, buf, len - 1);
361d4afb5ceSopenharmony_ci	close(fd);
362d4afb5ceSopenharmony_ci	if (n < 0) {
363d4afb5ceSopenharmony_ci		buf[0] = '\0';
364d4afb5ceSopenharmony_ci		return -1;
365d4afb5ceSopenharmony_ci	}
366d4afb5ceSopenharmony_ci
367d4afb5ceSopenharmony_ci	buf[n] = '\0';
368d4afb5ceSopenharmony_ci	if (n > 0 && buf[n - 1] == '\n')
369d4afb5ceSopenharmony_ci		buf[--n] = '\0';
370d4afb5ceSopenharmony_ci
371d4afb5ceSopenharmony_ci	return n;
372d4afb5ceSopenharmony_ci}
373d4afb5ceSopenharmony_ci#endif
374d4afb5ceSopenharmony_ci
375d4afb5ceSopenharmony_ci/*
376d4afb5ceSopenharmony_ci * Let's pregenerate the output into an lwsac all at once and
377d4afb5ceSopenharmony_ci * then spool it back to the peer afterwards
378d4afb5ceSopenharmony_ci *
379d4afb5ceSopenharmony_ci * - there's not going to be that much of it (a few kB)
380d4afb5ceSopenharmony_ci * - we then know the content-length for the headers
381d4afb5ceSopenharmony_ci * - it's stretchy to arbitrary numbers of metrics
382d4afb5ceSopenharmony_ci * - lwsac block list provides the per-metric structure to
383d4afb5ceSopenharmony_ci *   hold the data in a way we can walk to write it simply
384d4afb5ceSopenharmony_ci */
385d4afb5ceSopenharmony_ci
386d4afb5ceSopenharmony_ciint
387d4afb5ceSopenharmony_ciome_prepare(struct lws_context *ctx, struct pss *pss)
388d4afb5ceSopenharmony_ci{
389d4afb5ceSopenharmony_ci	char buf[1224], *start = buf + LWS_PRE, *p = start,
390d4afb5ceSopenharmony_ci	     *end = buf + sizeof(buf) - 1;
391d4afb5ceSopenharmony_ci	char hn[64];
392d4afb5ceSopenharmony_ci
393d4afb5ceSopenharmony_ci	pss->tot = 0;
394d4afb5ceSopenharmony_ci
395d4afb5ceSopenharmony_ci	/*
396d4afb5ceSopenharmony_ci	 * Target metadata
397d4afb5ceSopenharmony_ci	 */
398d4afb5ceSopenharmony_ci
399d4afb5ceSopenharmony_ci	hn[0] = '\0';
400d4afb5ceSopenharmony_ci	gethostname(hn, sizeof(hn) - 1);
401d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
402d4afb5ceSopenharmony_ci			  "# TYPE target info\n"
403d4afb5ceSopenharmony_ci			  "# HELP target Target metadata\n"
404d4afb5ceSopenharmony_ci			  "target_info{hostname=\"%s\"", hn);
405d4afb5ceSopenharmony_ci
406d4afb5ceSopenharmony_ci#if defined(__linux__)
407d4afb5ceSopenharmony_ci	if (grabfile("/proc/self/cmdline", hn, sizeof(hn)))
408d4afb5ceSopenharmony_ci		p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
409d4afb5ceSopenharmony_ci				  ",cmdline=\"%s\"", hn);
410d4afb5ceSopenharmony_ci#endif
411d4afb5ceSopenharmony_ci
412d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "} 1\n");
413d4afb5ceSopenharmony_ci
414d4afb5ceSopenharmony_ci	if (lws_metrics_om_ac_stash(pss, (const char *)buf + LWS_PRE,
415d4afb5ceSopenharmony_ci				    lws_ptr_diff_size_t(p, buf + LWS_PRE)))
416d4afb5ceSopenharmony_ci		return 1;
417d4afb5ceSopenharmony_ci
418d4afb5ceSopenharmony_ci	/* lws version */
419d4afb5ceSopenharmony_ci
420d4afb5ceSopenharmony_ci	p = start;
421d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
422d4afb5ceSopenharmony_ci			  "# TYPE lws_info info\n"
423d4afb5ceSopenharmony_ci			  "# HELP lws_info Version of lws producing this\n"
424d4afb5ceSopenharmony_ci			  "lws_info{version=\"%s\"} 1\n", LWS_BUILD_HASH);
425d4afb5ceSopenharmony_ci	if (lws_metrics_om_ac_stash(pss, (const char *)buf + LWS_PRE,
426d4afb5ceSopenharmony_ci				    lws_ptr_diff_size_t(p, buf + LWS_PRE)))
427d4afb5ceSopenharmony_ci		return 1;
428d4afb5ceSopenharmony_ci
429d4afb5ceSopenharmony_ci	/* system scalars */
430d4afb5ceSopenharmony_ci
431d4afb5ceSopenharmony_ci#if defined(__linux__)
432d4afb5ceSopenharmony_ci	if (grabfile("/proc/loadavg", hn, sizeof(hn))) {
433d4afb5ceSopenharmony_ci		char *sp = strchr(hn, ' ');
434d4afb5ceSopenharmony_ci		if (sp) {
435d4afb5ceSopenharmony_ci			p = start;
436d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
437d4afb5ceSopenharmony_ci					  "load_1m %.*s\n",
438d4afb5ceSopenharmony_ci					  lws_ptr_diff(sp, hn), hn);
439d4afb5ceSopenharmony_ci			if (lws_metrics_om_ac_stash(pss,
440d4afb5ceSopenharmony_ci						    (char *)buf + LWS_PRE,
441d4afb5ceSopenharmony_ci						    lws_ptr_diff_size_t(p,
442d4afb5ceSopenharmony_ci								start)))
443d4afb5ceSopenharmony_ci				return 1;
444d4afb5ceSopenharmony_ci		}
445d4afb5ceSopenharmony_ci	}
446d4afb5ceSopenharmony_ci#endif
447d4afb5ceSopenharmony_ci
448d4afb5ceSopenharmony_ci	if (lws_metrics_foreach(ctx, pss, append_om_metric))
449d4afb5ceSopenharmony_ci		return 1;
450d4afb5ceSopenharmony_ci
451d4afb5ceSopenharmony_ci	p = start;
452d4afb5ceSopenharmony_ci	p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
453d4afb5ceSopenharmony_ci			  "# EOF\n");
454d4afb5ceSopenharmony_ci	if (lws_metrics_om_ac_stash(pss, (char *)buf + LWS_PRE,
455d4afb5ceSopenharmony_ci				    lws_ptr_diff_size_t(p, buf + LWS_PRE)))
456d4afb5ceSopenharmony_ci		return 1;
457d4afb5ceSopenharmony_ci
458d4afb5ceSopenharmony_ci	pss->walk = pss->ac;
459d4afb5ceSopenharmony_ci
460d4afb5ceSopenharmony_ci	return 0;
461d4afb5ceSopenharmony_ci}
462d4afb5ceSopenharmony_ci
463d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SERVER)
464d4afb5ceSopenharmony_ci
465d4afb5ceSopenharmony_ci/* 1) direct http export for scraper */
466d4afb5ceSopenharmony_ci
467d4afb5ceSopenharmony_cistatic int
468d4afb5ceSopenharmony_cicallback_lws_openmetrics_export(struct lws *wsi,
469d4afb5ceSopenharmony_ci				enum lws_callback_reasons reason,
470d4afb5ceSopenharmony_ci				void *user, void *in, size_t len)
471d4afb5ceSopenharmony_ci{
472d4afb5ceSopenharmony_ci	unsigned char buf[1224], *start = buf + LWS_PRE, *p = start,
473d4afb5ceSopenharmony_ci		      *end = buf + sizeof(buf) - 1, *ip;
474d4afb5ceSopenharmony_ci	struct lws_context *cx = lws_get_context(wsi);
475d4afb5ceSopenharmony_ci	struct pss *pss = (struct pss *)user;
476d4afb5ceSopenharmony_ci	unsigned int m, wm;
477d4afb5ceSopenharmony_ci
478d4afb5ceSopenharmony_ci	switch (reason) {
479d4afb5ceSopenharmony_ci	case LWS_CALLBACK_HTTP:
480d4afb5ceSopenharmony_ci
481d4afb5ceSopenharmony_ci		ome_prepare(cx, pss);
482d4afb5ceSopenharmony_ci
483d4afb5ceSopenharmony_ci		p = start;
484d4afb5ceSopenharmony_ci		if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
485d4afb5ceSopenharmony_ci						"application/openmetrics-text; "
486d4afb5ceSopenharmony_ci						"version=1.0.0; charset=utf-8",
487d4afb5ceSopenharmony_ci						pss->tot, &p, end) ||
488d4afb5ceSopenharmony_ci		    lws_finalize_write_http_header(wsi, start, &p, end))
489d4afb5ceSopenharmony_ci			return 1;
490d4afb5ceSopenharmony_ci
491d4afb5ceSopenharmony_ci		lws_callback_on_writable(wsi);
492d4afb5ceSopenharmony_ci
493d4afb5ceSopenharmony_ci		return 0;
494d4afb5ceSopenharmony_ci
495d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLOSED_HTTP:
496d4afb5ceSopenharmony_ci		lwsac_free(&pss->ac);
497d4afb5ceSopenharmony_ci		break;
498d4afb5ceSopenharmony_ci
499d4afb5ceSopenharmony_ci	case LWS_CALLBACK_HTTP_WRITEABLE:
500d4afb5ceSopenharmony_ci		if (!pss->walk)
501d4afb5ceSopenharmony_ci			return 0;
502d4afb5ceSopenharmony_ci
503d4afb5ceSopenharmony_ci		do {
504d4afb5ceSopenharmony_ci			ip = (uint8_t *)pss->walk +
505d4afb5ceSopenharmony_ci				lwsac_sizeof(pss->walk == pss->ac) + LWS_PRE;
506d4afb5ceSopenharmony_ci			m = (unsigned int)((ip[0] << 8) | ip[1]);
507d4afb5ceSopenharmony_ci
508d4afb5ceSopenharmony_ci			/* coverity */
509d4afb5ceSopenharmony_ci			if (m > lwsac_get_tail_pos(pss->walk) -
510d4afb5ceSopenharmony_ci				lwsac_sizeof(pss->walk == pss->ac))
511d4afb5ceSopenharmony_ci				return -1;
512d4afb5ceSopenharmony_ci
513d4afb5ceSopenharmony_ci			if (lws_ptr_diff_size_t(end, p) < m)
514d4afb5ceSopenharmony_ci				break;
515d4afb5ceSopenharmony_ci
516d4afb5ceSopenharmony_ci			memcpy(p, ip + 2, m);
517d4afb5ceSopenharmony_ci			p += m;
518d4afb5ceSopenharmony_ci
519d4afb5ceSopenharmony_ci			pss->walk = lwsac_get_next(pss->walk);
520d4afb5ceSopenharmony_ci		} while (pss->walk);
521d4afb5ceSopenharmony_ci
522d4afb5ceSopenharmony_ci		if (!lws_ptr_diff_size_t(p, start)) {
523d4afb5ceSopenharmony_ci			lwsl_err("%s: stuck\n", __func__);
524d4afb5ceSopenharmony_ci			return -1;
525d4afb5ceSopenharmony_ci		}
526d4afb5ceSopenharmony_ci
527d4afb5ceSopenharmony_ci		wm = pss->walk ? LWS_WRITE_HTTP : LWS_WRITE_HTTP_FINAL;
528d4afb5ceSopenharmony_ci
529d4afb5ceSopenharmony_ci		if (lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
530d4afb5ceSopenharmony_ci			      (enum lws_write_protocol)wm) < 0)
531d4afb5ceSopenharmony_ci			return 1;
532d4afb5ceSopenharmony_ci
533d4afb5ceSopenharmony_ci		if (!pss->walk) {
534d4afb5ceSopenharmony_ci			 if (lws_http_transaction_completed(wsi))
535d4afb5ceSopenharmony_ci				return -1;
536d4afb5ceSopenharmony_ci		} else
537d4afb5ceSopenharmony_ci			lws_callback_on_writable(wsi);
538d4afb5ceSopenharmony_ci
539d4afb5ceSopenharmony_ci		return 0;
540d4afb5ceSopenharmony_ci
541d4afb5ceSopenharmony_ci	default:
542d4afb5ceSopenharmony_ci		break;
543d4afb5ceSopenharmony_ci	}
544d4afb5ceSopenharmony_ci
545d4afb5ceSopenharmony_ci	return lws_callback_http_dummy(wsi, reason, user, in, len);
546d4afb5ceSopenharmony_ci}
547d4afb5ceSopenharmony_ci
548d4afb5ceSopenharmony_cistatic struct pss *
549d4afb5ceSopenharmony_ciomc_lws_om_get_other_side_pss_client(struct vhd *vhd, struct pss *pss)
550d4afb5ceSopenharmony_ci{
551d4afb5ceSopenharmony_ci	/*
552d4afb5ceSopenharmony_ci	 * Search through our partner's clients list looking for one with the
553d4afb5ceSopenharmony_ci	 * same proxy path
554d4afb5ceSopenharmony_ci	 */
555d4afb5ceSopenharmony_ci	lws_start_foreach_dll(struct lws_dll2 *, d,
556d4afb5ceSopenharmony_ci			vhd->bind_partner_vhd->clients.head) {
557d4afb5ceSopenharmony_ci		struct pss *apss = lws_container_of(d, struct pss, list);
558d4afb5ceSopenharmony_ci
559d4afb5ceSopenharmony_ci		if (!strcmp(pss->proxy_path, apss->proxy_path))
560d4afb5ceSopenharmony_ci			return apss;
561d4afb5ceSopenharmony_ci
562d4afb5ceSopenharmony_ci	} lws_end_foreach_dll(d);
563d4afb5ceSopenharmony_ci
564d4afb5ceSopenharmony_ci	return NULL;
565d4afb5ceSopenharmony_ci}
566d4afb5ceSopenharmony_ci
567d4afb5ceSopenharmony_ci/* 2) "lws-openmetrics-prox-agg": http server export via proxy to connected clients */
568d4afb5ceSopenharmony_ci
569d4afb5ceSopenharmony_cistatic int
570d4afb5ceSopenharmony_cicallback_lws_openmetrics_prox_agg(struct lws *wsi,
571d4afb5ceSopenharmony_ci				  enum lws_callback_reasons reason,
572d4afb5ceSopenharmony_ci				  void *user, void *in, size_t len)
573d4afb5ceSopenharmony_ci{
574d4afb5ceSopenharmony_ci	unsigned char buf[1224], *start = buf + LWS_PRE, *p = start,
575d4afb5ceSopenharmony_ci		      *end = buf + sizeof(buf) - 1, *ip;
576d4afb5ceSopenharmony_ci	struct vhd *vhd = (struct vhd *)lws_protocol_vh_priv_get(
577d4afb5ceSopenharmony_ci				lws_get_vhost(wsi), lws_get_protocol(wsi));
578d4afb5ceSopenharmony_ci	struct lws_context *cx = lws_get_context(wsi);
579d4afb5ceSopenharmony_ci	struct pss *pss = (struct pss *)user, *partner_pss;
580d4afb5ceSopenharmony_ci	unsigned int m, wm;
581d4afb5ceSopenharmony_ci
582d4afb5ceSopenharmony_ci	switch (reason) {
583d4afb5ceSopenharmony_ci
584d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_INIT:
585d4afb5ceSopenharmony_ci		lwsl_notice("%s: PROTOCOL_INIT on %s\n", __func__, lws_vh_tag(lws_get_vhost(wsi)));
586d4afb5ceSopenharmony_ci		/*
587d4afb5ceSopenharmony_ci		 * We get told what to do when we are bound to the vhost
588d4afb5ceSopenharmony_ci		 */
589d4afb5ceSopenharmony_ci		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
590d4afb5ceSopenharmony_ci				lws_get_protocol(wsi), sizeof(struct vhd));
591d4afb5ceSopenharmony_ci		if (!vhd) {
592d4afb5ceSopenharmony_ci			lwsl_err("%s: vhd alloc failed\n", __func__);
593d4afb5ceSopenharmony_ci			return 0;
594d4afb5ceSopenharmony_ci		}
595d4afb5ceSopenharmony_ci
596d4afb5ceSopenharmony_ci		vhd->cx = cx;
597d4afb5ceSopenharmony_ci
598d4afb5ceSopenharmony_ci		/*
599d4afb5ceSopenharmony_ci		 * Try to bind to the counterpart server in the proxy, binding
600d4afb5ceSopenharmony_ci		 * to the right one by having a common bind name set in a pvo.
601d4afb5ceSopenharmony_ci		 * We don't know who will get instantiated last, so both parts
602d4afb5ceSopenharmony_ci		 * try to bind if not already bound
603d4afb5ceSopenharmony_ci		 */
604d4afb5ceSopenharmony_ci
605d4afb5ceSopenharmony_ci		if (!lws_pvo_get_str(in, "proxy-side-bind-name",
606d4afb5ceSopenharmony_ci				     &vhd->proxy_side_bind_name)) {
607d4afb5ceSopenharmony_ci			/*
608d4afb5ceSopenharmony_ci			 * Attempt to find the vhd that belongs to a vhost
609d4afb5ceSopenharmony_ci			 * that has instantiated protocol
610d4afb5ceSopenharmony_ci			 * "lws-openmetrics-prox-server", and has set pvo
611d4afb5ceSopenharmony_ci			 * "proxy-side-bind-name" on it to whatever our
612d4afb5ceSopenharmony_ci			 * vhd->proxy_side_bind_name was also set to.
613d4afb5ceSopenharmony_ci			 *
614d4afb5ceSopenharmony_ci			 * If found, inform the two sides of the same proxy
615d4afb5ceSopenharmony_ci			 * what their partner vhd is
616d4afb5ceSopenharmony_ci			 */
617d4afb5ceSopenharmony_ci			lws_strncpy(vhd->sanity, "isagg", sizeof(vhd->sanity));
618d4afb5ceSopenharmony_ci			vhd->bind_partner_vhd = lws_vhd_find_by_pvo(cx,
619d4afb5ceSopenharmony_ci						"lws-openmetrics-prox-server",
620d4afb5ceSopenharmony_ci						"proxy-side-bind-name",
621d4afb5ceSopenharmony_ci						vhd->proxy_side_bind_name);
622d4afb5ceSopenharmony_ci			if (vhd->bind_partner_vhd) {
623d4afb5ceSopenharmony_ci				assert(!strcmp(vhd->bind_partner_vhd->sanity, "isws"));
624d4afb5ceSopenharmony_ci				lwsl_notice("%s: proxy binding OK\n", __func__);
625d4afb5ceSopenharmony_ci				vhd->bind_partner_vhd->bind_partner_vhd = vhd;
626d4afb5ceSopenharmony_ci			}
627d4afb5ceSopenharmony_ci		} else {
628d4afb5ceSopenharmony_ci			lwsl_warn("%s: proxy-side-bind-name required\n", __func__);
629d4afb5ceSopenharmony_ci			return 0;
630d4afb5ceSopenharmony_ci		}
631d4afb5ceSopenharmony_ci
632d4afb5ceSopenharmony_ci		break;
633d4afb5ceSopenharmony_ci
634d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_DESTROY:
635d4afb5ceSopenharmony_ci		if (vhd)
636d4afb5ceSopenharmony_ci			lws_sul_cancel(&vhd->sul);
637d4afb5ceSopenharmony_ci		break;
638d4afb5ceSopenharmony_ci
639d4afb5ceSopenharmony_ci	case LWS_CALLBACK_HTTP:
640d4afb5ceSopenharmony_ci
641d4afb5ceSopenharmony_ci		/*
642d4afb5ceSopenharmony_ci		 * The scraper has connected to us, the local side of the proxy,
643d4afb5ceSopenharmony_ci		 * we need to match what it wants to
644d4afb5ceSopenharmony_ci		 */
645d4afb5ceSopenharmony_ci
646d4afb5ceSopenharmony_ci		if (!vhd->bind_partner_vhd)
647d4afb5ceSopenharmony_ci			return 0;
648d4afb5ceSopenharmony_ci
649d4afb5ceSopenharmony_ci		lws_strnncpy(pss->proxy_path, (const char *)in, len,
650d4afb5ceSopenharmony_ci			     sizeof(pss->proxy_path));
651d4afb5ceSopenharmony_ci
652d4afb5ceSopenharmony_ci		if (pss->list.owner) {
653d4afb5ceSopenharmony_ci			lwsl_warn("%s: double HTTP?\n", __func__);
654d4afb5ceSopenharmony_ci			return 0;
655d4afb5ceSopenharmony_ci		}
656d4afb5ceSopenharmony_ci
657d4afb5ceSopenharmony_ci		pss->wsi = wsi;
658d4afb5ceSopenharmony_ci
659d4afb5ceSopenharmony_ci		lws_start_foreach_dll(struct lws_dll2 *, d,
660d4afb5ceSopenharmony_ci				      vhd->bind_partner_vhd->clients.head) {
661d4afb5ceSopenharmony_ci			struct pss *apss = lws_container_of(d, struct pss, list);
662d4afb5ceSopenharmony_ci
663d4afb5ceSopenharmony_ci			if (!strcmp((const char *)in, apss->proxy_path)) {
664d4afb5ceSopenharmony_ci				apss->trigger = 1;
665d4afb5ceSopenharmony_ci				lws_callback_on_writable(apss->wsi);
666d4afb5ceSopenharmony_ci
667d4afb5ceSopenharmony_ci				/* let's add him on the http server vhd list */
668d4afb5ceSopenharmony_ci
669d4afb5ceSopenharmony_ci				lws_dll2_add_tail(&pss->list, &vhd->clients);
670d4afb5ceSopenharmony_ci				return 0;
671d4afb5ceSopenharmony_ci			}
672d4afb5ceSopenharmony_ci
673d4afb5ceSopenharmony_ci		} lws_end_foreach_dll(d);
674d4afb5ceSopenharmony_ci
675d4afb5ceSopenharmony_ci		return 0;
676d4afb5ceSopenharmony_ci
677d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLOSED_HTTP:
678d4afb5ceSopenharmony_ci		lwsac_free(&pss->ac);
679d4afb5ceSopenharmony_ci		lws_dll2_remove(&pss->list);
680d4afb5ceSopenharmony_ci		break;
681d4afb5ceSopenharmony_ci
682d4afb5ceSopenharmony_ci	case LWS_CALLBACK_HTTP_WRITEABLE:
683d4afb5ceSopenharmony_ci
684d4afb5ceSopenharmony_ci		if (!pss->walk)
685d4afb5ceSopenharmony_ci			return 0;
686d4afb5ceSopenharmony_ci
687d4afb5ceSopenharmony_ci		/* locate the wss side if it's still around */
688d4afb5ceSopenharmony_ci
689d4afb5ceSopenharmony_ci		partner_pss = omc_lws_om_get_other_side_pss_client(vhd, pss);
690d4afb5ceSopenharmony_ci		if (!partner_pss)
691d4afb5ceSopenharmony_ci			return -1;
692d4afb5ceSopenharmony_ci
693d4afb5ceSopenharmony_ci		do {
694d4afb5ceSopenharmony_ci			ip = (uint8_t *)pss->walk +
695d4afb5ceSopenharmony_ci				lwsac_sizeof(pss->walk == partner_pss->ac) + LWS_PRE;
696d4afb5ceSopenharmony_ci			m = (unsigned int)((ip[0] << 8) | ip[1]);
697d4afb5ceSopenharmony_ci
698d4afb5ceSopenharmony_ci			/* coverity */
699d4afb5ceSopenharmony_ci			if (m > lwsac_get_tail_pos(pss->walk) -
700d4afb5ceSopenharmony_ci				lwsac_sizeof(pss->walk == partner_pss->ac))
701d4afb5ceSopenharmony_ci				return -1;
702d4afb5ceSopenharmony_ci
703d4afb5ceSopenharmony_ci			if (lws_ptr_diff_size_t(end, p) < m)
704d4afb5ceSopenharmony_ci				break;
705d4afb5ceSopenharmony_ci
706d4afb5ceSopenharmony_ci			memcpy(p, ip + 2, m);
707d4afb5ceSopenharmony_ci			p += m;
708d4afb5ceSopenharmony_ci
709d4afb5ceSopenharmony_ci			pss->walk = lwsac_get_next(pss->walk);
710d4afb5ceSopenharmony_ci		} while (pss->walk);
711d4afb5ceSopenharmony_ci
712d4afb5ceSopenharmony_ci		if (!lws_ptr_diff_size_t(p, start)) {
713d4afb5ceSopenharmony_ci			lwsl_err("%s: stuck\n", __func__);
714d4afb5ceSopenharmony_ci			return -1;
715d4afb5ceSopenharmony_ci		}
716d4afb5ceSopenharmony_ci
717d4afb5ceSopenharmony_ci		wm = pss->walk ? LWS_WRITE_HTTP : LWS_WRITE_HTTP_FINAL;
718d4afb5ceSopenharmony_ci
719d4afb5ceSopenharmony_ci		if (lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
720d4afb5ceSopenharmony_ci			      (enum lws_write_protocol)wm) < 0)
721d4afb5ceSopenharmony_ci			return 1;
722d4afb5ceSopenharmony_ci
723d4afb5ceSopenharmony_ci		if (!pss->walk) {
724d4afb5ceSopenharmony_ci			lwsl_info("%s: whole msg proxied to scraper\n", __func__);
725d4afb5ceSopenharmony_ci			lws_dll2_remove(&pss->list);
726d4afb5ceSopenharmony_ci			lwsac_free(&partner_pss->ac);
727d4afb5ceSopenharmony_ci//			if (lws_http_transaction_completed(wsi))
728d4afb5ceSopenharmony_ci			return -1;
729d4afb5ceSopenharmony_ci		} else
730d4afb5ceSopenharmony_ci			lws_callback_on_writable(wsi);
731d4afb5ceSopenharmony_ci
732d4afb5ceSopenharmony_ci		return 0;
733d4afb5ceSopenharmony_ci
734d4afb5ceSopenharmony_ci	default:
735d4afb5ceSopenharmony_ci		break;
736d4afb5ceSopenharmony_ci	}
737d4afb5ceSopenharmony_ci
738d4afb5ceSopenharmony_ci	return lws_callback_http_dummy(wsi, reason, user, in, len);
739d4afb5ceSopenharmony_ci}
740d4afb5ceSopenharmony_ci
741d4afb5ceSopenharmony_ci/* 3) "lws-openmetrics-prox-server": ws server side of metrics proxy, for
742d4afb5ceSopenharmony_ci *    ws clients to connect to */
743d4afb5ceSopenharmony_ci
744d4afb5ceSopenharmony_cistatic int
745d4afb5ceSopenharmony_cicallback_lws_openmetrics_prox_server(struct lws *wsi,
746d4afb5ceSopenharmony_ci				     enum lws_callback_reasons reason,
747d4afb5ceSopenharmony_ci				     void *user, void *in, size_t len)
748d4afb5ceSopenharmony_ci{
749d4afb5ceSopenharmony_ci	unsigned char buf[1224], *start = buf + LWS_PRE, *p = start,
750d4afb5ceSopenharmony_ci		      *end = buf + sizeof(buf) - 1;
751d4afb5ceSopenharmony_ci	struct vhd *vhd = (struct vhd *)lws_protocol_vh_priv_get(
752d4afb5ceSopenharmony_ci				lws_get_vhost(wsi), lws_get_protocol(wsi));
753d4afb5ceSopenharmony_ci	struct lws_context *cx = lws_get_context(wsi);
754d4afb5ceSopenharmony_ci	struct pss *pss = (struct pss *)user, *partner_pss;
755d4afb5ceSopenharmony_ci
756d4afb5ceSopenharmony_ci	switch (reason) {
757d4afb5ceSopenharmony_ci
758d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_INIT:
759d4afb5ceSopenharmony_ci		/*
760d4afb5ceSopenharmony_ci		 * We get told what to do when we are bound to the vhost
761d4afb5ceSopenharmony_ci		 */
762d4afb5ceSopenharmony_ci
763d4afb5ceSopenharmony_ci		lwsl_notice("%s: PROTOCOL_INIT on %s\n", __func__, lws_vh_tag(lws_get_vhost(wsi)));
764d4afb5ceSopenharmony_ci
765d4afb5ceSopenharmony_ci		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
766d4afb5ceSopenharmony_ci				lws_get_protocol(wsi), sizeof(struct vhd));
767d4afb5ceSopenharmony_ci		if (!vhd) {
768d4afb5ceSopenharmony_ci			lwsl_err("%s: vhd alloc failed\n", __func__);
769d4afb5ceSopenharmony_ci			return 0;
770d4afb5ceSopenharmony_ci		}
771d4afb5ceSopenharmony_ci
772d4afb5ceSopenharmony_ci		vhd->cx = cx;
773d4afb5ceSopenharmony_ci
774d4afb5ceSopenharmony_ci		/*
775d4afb5ceSopenharmony_ci		 * Try to bind to the counterpart server in the proxy, binding
776d4afb5ceSopenharmony_ci		 * to the right one by having a common bind name set in a pvo.
777d4afb5ceSopenharmony_ci		 * We don't know who will get instantiated last, so both parts
778d4afb5ceSopenharmony_ci		 * try to bind if not already bound
779d4afb5ceSopenharmony_ci		 */
780d4afb5ceSopenharmony_ci
781d4afb5ceSopenharmony_ci		if (!lws_pvo_get_str(in, "proxy-side-bind-name",
782d4afb5ceSopenharmony_ci				     &vhd->proxy_side_bind_name)) {
783d4afb5ceSopenharmony_ci			/*
784d4afb5ceSopenharmony_ci			 * Attempt to find the vhd that belongs to a vhost
785d4afb5ceSopenharmony_ci			 * that has instantiated protocol
786d4afb5ceSopenharmony_ci			 * "lws-openmetrics-prox-server", and has set pvo
787d4afb5ceSopenharmony_ci			 * "proxy-side-bind-name" on it to whatever our
788d4afb5ceSopenharmony_ci			 * vhd->proxy_side_bind_name was also set to.
789d4afb5ceSopenharmony_ci			 *
790d4afb5ceSopenharmony_ci			 * If found, inform the two sides of the same proxy
791d4afb5ceSopenharmony_ci			 * what their partner vhd is
792d4afb5ceSopenharmony_ci			 */
793d4afb5ceSopenharmony_ci			lws_strncpy(vhd->sanity, "isws", sizeof(vhd->sanity));
794d4afb5ceSopenharmony_ci			vhd->bind_partner_vhd = lws_vhd_find_by_pvo(cx,
795d4afb5ceSopenharmony_ci						"lws-openmetrics-prox-agg",
796d4afb5ceSopenharmony_ci						"proxy-side-bind-name",
797d4afb5ceSopenharmony_ci						vhd->proxy_side_bind_name);
798d4afb5ceSopenharmony_ci			if (vhd->bind_partner_vhd) {
799d4afb5ceSopenharmony_ci				assert(!strcmp(vhd->bind_partner_vhd->sanity, "isagg"));
800d4afb5ceSopenharmony_ci				lwsl_notice("%s: proxy binding OK\n", __func__);
801d4afb5ceSopenharmony_ci				vhd->bind_partner_vhd->bind_partner_vhd = vhd;
802d4afb5ceSopenharmony_ci			}
803d4afb5ceSopenharmony_ci		} else {
804d4afb5ceSopenharmony_ci			lwsl_warn("%s: proxy-side-bind-name required\n", __func__);
805d4afb5ceSopenharmony_ci			return 0;
806d4afb5ceSopenharmony_ci		}
807d4afb5ceSopenharmony_ci
808d4afb5ceSopenharmony_ci		break;
809d4afb5ceSopenharmony_ci
810d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_DESTROY:
811d4afb5ceSopenharmony_ci		break;
812d4afb5ceSopenharmony_ci
813d4afb5ceSopenharmony_ci	case LWS_CALLBACK_ESTABLISHED:
814d4afb5ceSopenharmony_ci		/*
815d4afb5ceSopenharmony_ci		 * a client has joined... we need to add his pss to our list
816d4afb5ceSopenharmony_ci		 * of live, joined clients
817d4afb5ceSopenharmony_ci		 */
818d4afb5ceSopenharmony_ci
819d4afb5ceSopenharmony_ci		/* mark us as waiting for the reference name from the client */
820d4afb5ceSopenharmony_ci		pss->greet = 1;
821d4afb5ceSopenharmony_ci		pss->wsi = wsi;
822d4afb5ceSopenharmony_ci		lws_validity_confirmed(wsi);
823d4afb5ceSopenharmony_ci
824d4afb5ceSopenharmony_ci		return 0;
825d4afb5ceSopenharmony_ci
826d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLOSED:
827d4afb5ceSopenharmony_ci		/*
828d4afb5ceSopenharmony_ci		 * a client has parted
829d4afb5ceSopenharmony_ci		 */
830d4afb5ceSopenharmony_ci		lws_dll2_remove(&pss->list);
831d4afb5ceSopenharmony_ci		lwsl_warn("%s: client %s left (%u)\n", __func__,
832d4afb5ceSopenharmony_ci				pss->proxy_path,
833d4afb5ceSopenharmony_ci				(unsigned int)vhd->clients.count);
834d4afb5ceSopenharmony_ci		lwsac_free(&pss->ac);
835d4afb5ceSopenharmony_ci
836d4afb5ceSopenharmony_ci		/* let's kill the scraper connection accordingly, if still up */
837d4afb5ceSopenharmony_ci		partner_pss = omc_lws_om_get_other_side_pss_client(vhd, pss);
838d4afb5ceSopenharmony_ci		if (partner_pss)
839d4afb5ceSopenharmony_ci			lws_wsi_close(partner_pss->wsi, LWS_TO_KILL_ASYNC);
840d4afb5ceSopenharmony_ci		break;
841d4afb5ceSopenharmony_ci
842d4afb5ceSopenharmony_ci	case LWS_CALLBACK_RECEIVE:
843d4afb5ceSopenharmony_ci		if (pss->greet) {
844d4afb5ceSopenharmony_ci			pss->greet = 0;
845d4afb5ceSopenharmony_ci			lws_strnncpy(pss->proxy_path, (const char *)in, len,
846d4afb5ceSopenharmony_ci				     sizeof(pss->proxy_path));
847d4afb5ceSopenharmony_ci
848d4afb5ceSopenharmony_ci			lws_validity_confirmed(wsi);
849d4afb5ceSopenharmony_ci			lwsl_notice("%s: received greet '%s'\n", __func__,
850d4afb5ceSopenharmony_ci				    pss->proxy_path);
851d4afb5ceSopenharmony_ci			/*
852d4afb5ceSopenharmony_ci			 * we need to add his pss to our list of configured,
853d4afb5ceSopenharmony_ci			 * live, joined clients
854d4afb5ceSopenharmony_ci			 */
855d4afb5ceSopenharmony_ci			lws_dll2_add_tail(&pss->list, &vhd->clients);
856d4afb5ceSopenharmony_ci			return 0;
857d4afb5ceSopenharmony_ci		}
858d4afb5ceSopenharmony_ci
859d4afb5ceSopenharmony_ci		/*
860d4afb5ceSopenharmony_ci		 * He's sending us his results... let's collect chunks into the
861d4afb5ceSopenharmony_ci		 * pss lwsac before worrying about anything else
862d4afb5ceSopenharmony_ci		 */
863d4afb5ceSopenharmony_ci
864d4afb5ceSopenharmony_ci		if (lws_is_first_fragment(wsi))
865d4afb5ceSopenharmony_ci			pss->tot = 0;
866d4afb5ceSopenharmony_ci
867d4afb5ceSopenharmony_ci		lws_metrics_om_ac_stash(pss, (const char *)in, len);
868d4afb5ceSopenharmony_ci
869d4afb5ceSopenharmony_ci		if (lws_is_final_fragment(wsi)) {
870d4afb5ceSopenharmony_ci			struct pss *partner_pss;
871d4afb5ceSopenharmony_ci
872d4afb5ceSopenharmony_ci			lwsl_info("%s: ws side received complete msg\n",
873d4afb5ceSopenharmony_ci					__func__);
874d4afb5ceSopenharmony_ci
875d4afb5ceSopenharmony_ci			/* the lwsac is complete */
876d4afb5ceSopenharmony_ci			pss->walk = pss->ac;
877d4afb5ceSopenharmony_ci			partner_pss = omc_lws_om_get_other_side_pss_client(vhd, pss);
878d4afb5ceSopenharmony_ci			if (!partner_pss) {
879d4afb5ceSopenharmony_ci				lwsl_notice("%s: no partner A\n", __func__);
880d4afb5ceSopenharmony_ci				return -1;
881d4afb5ceSopenharmony_ci			}
882d4afb5ceSopenharmony_ci
883d4afb5ceSopenharmony_ci			/* indicate to scraper side we want to issue now */
884d4afb5ceSopenharmony_ci
885d4afb5ceSopenharmony_ci			p = start;
886d4afb5ceSopenharmony_ci			if (lws_add_http_common_headers(partner_pss->wsi, HTTP_STATUS_OK,
887d4afb5ceSopenharmony_ci							"application/openmetrics-text; "
888d4afb5ceSopenharmony_ci							"version=1.0.0; charset=utf-8",
889d4afb5ceSopenharmony_ci							pss->tot, &p, end) ||
890d4afb5ceSopenharmony_ci			    lws_finalize_write_http_header(partner_pss->wsi,
891d4afb5ceSopenharmony_ci							    start, &p, end))
892d4afb5ceSopenharmony_ci				return -1;
893d4afb5ceSopenharmony_ci
894d4afb5ceSopenharmony_ci			/* indicate to scraper side we want to issue now */
895d4afb5ceSopenharmony_ci
896d4afb5ceSopenharmony_ci			partner_pss->walk = pss->ac;
897d4afb5ceSopenharmony_ci			partner_pss->trigger = 1;
898d4afb5ceSopenharmony_ci			lws_callback_on_writable(partner_pss->wsi);
899d4afb5ceSopenharmony_ci		}
900d4afb5ceSopenharmony_ci
901d4afb5ceSopenharmony_ci		return 0;
902d4afb5ceSopenharmony_ci
903d4afb5ceSopenharmony_ci	case LWS_CALLBACK_SERVER_WRITEABLE:
904d4afb5ceSopenharmony_ci		if (!pss->trigger)
905d4afb5ceSopenharmony_ci			return 0;
906d4afb5ceSopenharmony_ci
907d4afb5ceSopenharmony_ci		pss->trigger = 0;
908d4afb5ceSopenharmony_ci
909d4afb5ceSopenharmony_ci		partner_pss = omc_lws_om_get_other_side_pss_client(vhd, pss);
910d4afb5ceSopenharmony_ci		if (!partner_pss) {
911d4afb5ceSopenharmony_ci			lwsl_err("%s: no partner\n", __func__);
912d4afb5ceSopenharmony_ci			return 0;
913d4afb5ceSopenharmony_ci		}
914d4afb5ceSopenharmony_ci
915d4afb5ceSopenharmony_ci		lwsl_info("%s: sending trigger to client\n", __func__);
916d4afb5ceSopenharmony_ci
917d4afb5ceSopenharmony_ci		*start = 'x';
918d4afb5ceSopenharmony_ci		if (lws_write(wsi, start, 1,
919d4afb5ceSopenharmony_ci			      (enum lws_write_protocol)LWS_WRITE_TEXT) < 0)
920d4afb5ceSopenharmony_ci			return 1;
921d4afb5ceSopenharmony_ci
922d4afb5ceSopenharmony_ci		lws_validity_confirmed(wsi);
923d4afb5ceSopenharmony_ci
924d4afb5ceSopenharmony_ci		return 0;
925d4afb5ceSopenharmony_ci
926d4afb5ceSopenharmony_ci	default:
927d4afb5ceSopenharmony_ci		break;
928d4afb5ceSopenharmony_ci	}
929d4afb5ceSopenharmony_ci
930d4afb5ceSopenharmony_ci	return lws_callback_http_dummy(wsi, reason, user, in, len);
931d4afb5ceSopenharmony_ci}
932d4afb5ceSopenharmony_ci#endif
933d4afb5ceSopenharmony_ci
934d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CLIENT) && defined(LWS_ROLE_WS)
935d4afb5ceSopenharmony_ci
936d4afb5ceSopenharmony_ci/* 4) ws client that keeps wss connection up to metrics proxy ws server */
937d4afb5ceSopenharmony_ci
938d4afb5ceSopenharmony_cistatic int
939d4afb5ceSopenharmony_cicallback_lws_openmetrics_prox_client(struct lws *wsi,
940d4afb5ceSopenharmony_ci				     enum lws_callback_reasons reason,
941d4afb5ceSopenharmony_ci				     void *user, void *in, size_t len)
942d4afb5ceSopenharmony_ci{
943d4afb5ceSopenharmony_ci	unsigned char buf[1224], *start = buf + LWS_PRE, *p = start,
944d4afb5ceSopenharmony_ci		      *end = buf + sizeof(buf) - 1, *ip;
945d4afb5ceSopenharmony_ci	struct vhd *vhd = (struct vhd *)lws_protocol_vh_priv_get(
946d4afb5ceSopenharmony_ci				lws_get_vhost(wsi), lws_get_protocol(wsi));
947d4afb5ceSopenharmony_ci	struct lws_context *cx = lws_get_context(wsi);
948d4afb5ceSopenharmony_ci	struct pss *pss = (struct pss *)user;
949d4afb5ceSopenharmony_ci	unsigned int m, wm;
950d4afb5ceSopenharmony_ci	const char *cp;
951d4afb5ceSopenharmony_ci	char first;
952d4afb5ceSopenharmony_ci
953d4afb5ceSopenharmony_ci	switch (reason) {
954d4afb5ceSopenharmony_ci
955d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_INIT:
956d4afb5ceSopenharmony_ci
957d4afb5ceSopenharmony_ci		lwsl_notice("%s: PROTOCOL_INIT on %s\n", __func__,
958d4afb5ceSopenharmony_ci					lws_vh_tag(lws_get_vhost(wsi)));
959d4afb5ceSopenharmony_ci
960d4afb5ceSopenharmony_ci
961d4afb5ceSopenharmony_ci		/*
962d4afb5ceSopenharmony_ci		 * We get told what to do when we are bound to the vhost
963d4afb5ceSopenharmony_ci		 */
964d4afb5ceSopenharmony_ci		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
965d4afb5ceSopenharmony_ci				lws_get_protocol(wsi), sizeof(struct vhd));
966d4afb5ceSopenharmony_ci		if (!vhd)
967d4afb5ceSopenharmony_ci			return 0;
968d4afb5ceSopenharmony_ci
969d4afb5ceSopenharmony_ci		vhd->cx = cx;
970d4afb5ceSopenharmony_ci		vhd->vhost = lws_get_vhost(wsi);
971d4afb5ceSopenharmony_ci
972d4afb5ceSopenharmony_ci		/* the proxy server uri */
973d4afb5ceSopenharmony_ci
974d4afb5ceSopenharmony_ci		if (lws_pvo_get_str(in, "ws-server-uri", &cp) || !cp) {
975d4afb5ceSopenharmony_ci			lwsl_warn("%s: ws-server-uri pvo required\n", __func__);
976d4afb5ceSopenharmony_ci
977d4afb5ceSopenharmony_ci			return 0;
978d4afb5ceSopenharmony_ci		}
979d4afb5ceSopenharmony_ci		lws_strncpy(vhd->ws_server_uri, cp, sizeof(vhd->ws_server_uri));
980d4afb5ceSopenharmony_ci
981d4afb5ceSopenharmony_ci		/* how we should be referenced at the proxy */
982d4afb5ceSopenharmony_ci
983d4afb5ceSopenharmony_ci		if (lws_pvo_get_str(in, "metrics-proxy-path", &cp)) {
984d4afb5ceSopenharmony_ci			lwsl_err("%s: metrics-proxy-path pvo required\n", __func__);
985d4afb5ceSopenharmony_ci
986d4afb5ceSopenharmony_ci			return 1;
987d4afb5ceSopenharmony_ci		}
988d4afb5ceSopenharmony_ci		lws_strncpy(vhd->metrics_proxy_path, cp, sizeof(vhd->metrics_proxy_path));
989d4afb5ceSopenharmony_ci
990d4afb5ceSopenharmony_ci		/* the shared secret to authenticate us as allowed to join */
991d4afb5ceSopenharmony_ci
992d4afb5ceSopenharmony_ci		if (lws_pvo_get_str(in, "ba-secret", &cp)) {
993d4afb5ceSopenharmony_ci			lwsl_err("%s: ba-secret pvo required\n", __func__);
994d4afb5ceSopenharmony_ci
995d4afb5ceSopenharmony_ci			return 1;
996d4afb5ceSopenharmony_ci		}
997d4afb5ceSopenharmony_ci		lws_strncpy(vhd->ba_secret, cp, sizeof(vhd->ba_secret));
998d4afb5ceSopenharmony_ci
999d4afb5ceSopenharmony_ci		lwsl_notice("%s: scheduling connect %s %s %s\n", __func__,
1000d4afb5ceSopenharmony_ci				vhd->ws_server_uri, vhd->metrics_proxy_path, vhd->ba_secret);
1001d4afb5ceSopenharmony_ci
1002d4afb5ceSopenharmony_ci		lws_validity_confirmed(wsi);
1003d4afb5ceSopenharmony_ci		lws_sul_schedule(cx, 0, &vhd->sul, omc_connect_client, 1);
1004d4afb5ceSopenharmony_ci		break;
1005d4afb5ceSopenharmony_ci
1006d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_DESTROY:
1007d4afb5ceSopenharmony_ci		if (vhd)
1008d4afb5ceSopenharmony_ci			lws_sul_cancel(&vhd->sul);
1009d4afb5ceSopenharmony_ci		break;
1010d4afb5ceSopenharmony_ci
1011d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
1012d4afb5ceSopenharmony_ci	{
1013d4afb5ceSopenharmony_ci		unsigned char **pp = (unsigned char **)in, *pend = (*pp) + len;
1014d4afb5ceSopenharmony_ci		char b[128];
1015d4afb5ceSopenharmony_ci
1016d4afb5ceSopenharmony_ci		/* authorize ourselves to the metrics proxy using basic auth */
1017d4afb5ceSopenharmony_ci
1018d4afb5ceSopenharmony_ci		if (lws_http_basic_auth_gen("metricsclient", vhd->ba_secret,
1019d4afb5ceSopenharmony_ci					    b, sizeof(b)))
1020d4afb5ceSopenharmony_ci			break;
1021d4afb5ceSopenharmony_ci
1022d4afb5ceSopenharmony_ci		if (lws_add_http_header_by_token(wsi,
1023d4afb5ceSopenharmony_ci						 WSI_TOKEN_HTTP_AUTHORIZATION,
1024d4afb5ceSopenharmony_ci						 (unsigned char *)b,
1025d4afb5ceSopenharmony_ci						 (int)strlen(b), pp, pend))
1026d4afb5ceSopenharmony_ci			return -1;
1027d4afb5ceSopenharmony_ci
1028d4afb5ceSopenharmony_ci		break;
1029d4afb5ceSopenharmony_ci	}
1030d4afb5ceSopenharmony_ci
1031d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
1032d4afb5ceSopenharmony_ci		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
1033d4afb5ceSopenharmony_ci			 in ? (char *)in : "(null)");
1034d4afb5ceSopenharmony_ci		goto do_retry;
1035d4afb5ceSopenharmony_ci
1036d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_ESTABLISHED:
1037d4afb5ceSopenharmony_ci		lwsl_warn("%s: connected to ws metrics agg server\n", __func__);
1038d4afb5ceSopenharmony_ci		pss->greet = 1;
1039d4afb5ceSopenharmony_ci		lws_callback_on_writable(wsi);
1040d4afb5ceSopenharmony_ci		lws_validity_confirmed(wsi);
1041d4afb5ceSopenharmony_ci		return 0;
1042d4afb5ceSopenharmony_ci
1043d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_CLOSED:
1044d4afb5ceSopenharmony_ci		lwsl_notice("%s: client closed\n", __func__);
1045d4afb5ceSopenharmony_ci		lwsac_free(&pss->ac);
1046d4afb5ceSopenharmony_ci		goto do_retry;
1047d4afb5ceSopenharmony_ci
1048d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_RECEIVE:
1049d4afb5ceSopenharmony_ci		/*
1050d4afb5ceSopenharmony_ci		 * Proxy serverside sends us something to trigger us to create
1051d4afb5ceSopenharmony_ci		 * our metrics message and send it back over the ws link
1052d4afb5ceSopenharmony_ci		 */
1053d4afb5ceSopenharmony_ci		ome_prepare(cx, pss);
1054d4afb5ceSopenharmony_ci		pss->walk = pss->ac;
1055d4afb5ceSopenharmony_ci		lws_callback_on_writable(wsi);
1056d4afb5ceSopenharmony_ci		lwsl_info("%s: dump requested\n", __func__);
1057d4afb5ceSopenharmony_ci		break;
1058d4afb5ceSopenharmony_ci
1059d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLIENT_WRITEABLE:
1060d4afb5ceSopenharmony_ci		if (pss->greet) {
1061d4afb5ceSopenharmony_ci			/*
1062d4afb5ceSopenharmony_ci			 * At first after establishing the we link, we send a
1063d4afb5ceSopenharmony_ci			 * message indicating to the metrics proxy how we
1064d4afb5ceSopenharmony_ci			 * should be referred to by the scraper to particularly
1065d4afb5ceSopenharmony_ci			 * select to talk to us
1066d4afb5ceSopenharmony_ci			 */
1067d4afb5ceSopenharmony_ci			lwsl_info("%s: sending greet '%s'\n", __func__,
1068d4afb5ceSopenharmony_ci					vhd->metrics_proxy_path);
1069d4afb5ceSopenharmony_ci			lws_strncpy((char *)start, vhd->metrics_proxy_path,
1070d4afb5ceSopenharmony_ci					sizeof(buf) - LWS_PRE);
1071d4afb5ceSopenharmony_ci			if (lws_write(wsi, start,
1072d4afb5ceSopenharmony_ci				      strlen(vhd->metrics_proxy_path),
1073d4afb5ceSopenharmony_ci				      LWS_WRITE_TEXT) < 0)
1074d4afb5ceSopenharmony_ci				return 1;
1075d4afb5ceSopenharmony_ci
1076d4afb5ceSopenharmony_ci			lws_validity_confirmed(wsi);
1077d4afb5ceSopenharmony_ci
1078d4afb5ceSopenharmony_ci			pss->greet = 0;
1079d4afb5ceSopenharmony_ci			return 0;
1080d4afb5ceSopenharmony_ci		}
1081d4afb5ceSopenharmony_ci
1082d4afb5ceSopenharmony_ci		if (!pss->walk)
1083d4afb5ceSopenharmony_ci			return 0;
1084d4afb5ceSopenharmony_ci
1085d4afb5ceSopenharmony_ci		/*
1086d4afb5ceSopenharmony_ci		 * We send the metrics dump in a single logical ws message,
1087d4afb5ceSopenharmony_ci		 * using ws fragmentation to split it around 1 mtu boundary
1088d4afb5ceSopenharmony_ci		 * and keep coming back until it's finished
1089d4afb5ceSopenharmony_ci		 */
1090d4afb5ceSopenharmony_ci
1091d4afb5ceSopenharmony_ci		first = pss->walk == pss->ac;
1092d4afb5ceSopenharmony_ci
1093d4afb5ceSopenharmony_ci		do {
1094d4afb5ceSopenharmony_ci			ip = (uint8_t *)pss->walk +
1095d4afb5ceSopenharmony_ci				lwsac_sizeof(pss->walk == pss->ac) + LWS_PRE;
1096d4afb5ceSopenharmony_ci			m = (unsigned int)((ip[0] << 8) | ip[1]);
1097d4afb5ceSopenharmony_ci
1098d4afb5ceSopenharmony_ci			/* coverity */
1099d4afb5ceSopenharmony_ci			if (m > lwsac_get_tail_pos(pss->walk) -
1100d4afb5ceSopenharmony_ci				lwsac_sizeof(pss->walk == pss->ac)) {
1101d4afb5ceSopenharmony_ci				lwsl_err("%s: size blow\n", __func__);
1102d4afb5ceSopenharmony_ci				return -1;
1103d4afb5ceSopenharmony_ci			}
1104d4afb5ceSopenharmony_ci
1105d4afb5ceSopenharmony_ci			if (lws_ptr_diff_size_t(end, p) < m)
1106d4afb5ceSopenharmony_ci				break;
1107d4afb5ceSopenharmony_ci
1108d4afb5ceSopenharmony_ci			memcpy(p, ip + 2, m);
1109d4afb5ceSopenharmony_ci			p += m;
1110d4afb5ceSopenharmony_ci
1111d4afb5ceSopenharmony_ci			pss->walk = lwsac_get_next(pss->walk);
1112d4afb5ceSopenharmony_ci		} while (pss->walk);
1113d4afb5ceSopenharmony_ci
1114d4afb5ceSopenharmony_ci		if (!lws_ptr_diff_size_t(p, start)) {
1115d4afb5ceSopenharmony_ci			lwsl_err("%s: stuck\n", __func__);
1116d4afb5ceSopenharmony_ci			return -1;
1117d4afb5ceSopenharmony_ci		}
1118d4afb5ceSopenharmony_ci
1119d4afb5ceSopenharmony_ci		wm = (unsigned int)lws_write_ws_flags(LWS_WRITE_TEXT, first,
1120d4afb5ceSopenharmony_ci						      !pss->walk);
1121d4afb5ceSopenharmony_ci
1122d4afb5ceSopenharmony_ci		if (lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
1123d4afb5ceSopenharmony_ci			      (enum lws_write_protocol)wm) < 0) {
1124d4afb5ceSopenharmony_ci			lwsl_notice("%s: write fail\n", __func__);
1125d4afb5ceSopenharmony_ci			return 1;
1126d4afb5ceSopenharmony_ci		}
1127d4afb5ceSopenharmony_ci
1128d4afb5ceSopenharmony_ci		lws_validity_confirmed(wsi);
1129d4afb5ceSopenharmony_ci		lwsl_info("%s: forwarded %d\n", __func__, lws_ptr_diff(p, start));
1130d4afb5ceSopenharmony_ci
1131d4afb5ceSopenharmony_ci		if (!pss->walk) {
1132d4afb5ceSopenharmony_ci			lwsl_info("%s: dump send completed\n", __func__);
1133d4afb5ceSopenharmony_ci			lwsac_free(&pss->ac);
1134d4afb5ceSopenharmony_ci		} else
1135d4afb5ceSopenharmony_ci			lws_callback_on_writable(wsi);
1136d4afb5ceSopenharmony_ci
1137d4afb5ceSopenharmony_ci		return 0;
1138d4afb5ceSopenharmony_ci
1139d4afb5ceSopenharmony_ci	default:
1140d4afb5ceSopenharmony_ci		break;
1141d4afb5ceSopenharmony_ci	}
1142d4afb5ceSopenharmony_ci
1143d4afb5ceSopenharmony_ci	return lws_callback_http_dummy(wsi, reason, user, in, len);
1144d4afb5ceSopenharmony_ci
1145d4afb5ceSopenharmony_cido_retry:
1146d4afb5ceSopenharmony_ci	if (!lws_retry_sul_schedule(cx, 0, &vhd->sul, &retry,
1147d4afb5ceSopenharmony_ci				    omc_connect_client, &vhd->retry_count))
1148d4afb5ceSopenharmony_ci		return 0;
1149d4afb5ceSopenharmony_ci
1150d4afb5ceSopenharmony_ci	vhd->retry_count = 0;
1151d4afb5ceSopenharmony_ci	lws_retry_sul_schedule(cx, 0, &vhd->sul, &retry,
1152d4afb5ceSopenharmony_ci			       omc_connect_client, &vhd->retry_count);
1153d4afb5ceSopenharmony_ci
1154d4afb5ceSopenharmony_ci	return 0;
1155d4afb5ceSopenharmony_ci}
1156d4afb5ceSopenharmony_ci#endif
1157d4afb5ceSopenharmony_ci
1158d4afb5ceSopenharmony_ci
1159d4afb5ceSopenharmony_ciLWS_VISIBLE const struct lws_protocols lws_openmetrics_export_protocols[] = {
1160d4afb5ceSopenharmony_ci#if defined(LWS_WITH_SERVER)
1161d4afb5ceSopenharmony_ci	{ /* for scraper directly: http export on listen socket */
1162d4afb5ceSopenharmony_ci		"lws-openmetrics",
1163d4afb5ceSopenharmony_ci		callback_lws_openmetrics_export,
1164d4afb5ceSopenharmony_ci		sizeof(struct pss),
1165d4afb5ceSopenharmony_ci		1024, 0, NULL, 0
1166d4afb5ceSopenharmony_ci	},
1167d4afb5ceSopenharmony_ci	{ /* for scraper via ws proxy: http export on listen socket */
1168d4afb5ceSopenharmony_ci		"lws-openmetrics-prox-agg",
1169d4afb5ceSopenharmony_ci		callback_lws_openmetrics_prox_agg,
1170d4afb5ceSopenharmony_ci		sizeof(struct pss),
1171d4afb5ceSopenharmony_ci		1024, 0, NULL, 0
1172d4afb5ceSopenharmony_ci	},
1173d4afb5ceSopenharmony_ci	{ /* metrics proxy server side: ws server for clients to connect to */
1174d4afb5ceSopenharmony_ci		"lws-openmetrics-prox-server",
1175d4afb5ceSopenharmony_ci		callback_lws_openmetrics_prox_server,
1176d4afb5ceSopenharmony_ci		sizeof(struct pss),
1177d4afb5ceSopenharmony_ci		1024, 0, NULL, 0
1178d4afb5ceSopenharmony_ci	},
1179d4afb5ceSopenharmony_ci#endif
1180d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CLIENT) && defined(LWS_ROLE_WS)
1181d4afb5ceSopenharmony_ci	{ /* client to metrics proxy: ws client to connect to metrics proxy*/
1182d4afb5ceSopenharmony_ci		"lws-openmetrics-prox-client",
1183d4afb5ceSopenharmony_ci		callback_lws_openmetrics_prox_client,
1184d4afb5ceSopenharmony_ci		sizeof(struct pss),
1185d4afb5ceSopenharmony_ci		1024, 0, NULL, 0
1186d4afb5ceSopenharmony_ci	},
1187d4afb5ceSopenharmony_ci#endif
1188d4afb5ceSopenharmony_ci};
1189d4afb5ceSopenharmony_ci
1190d4afb5ceSopenharmony_ciLWS_VISIBLE const lws_plugin_protocol_t lws_openmetrics_export = {
1191d4afb5ceSopenharmony_ci	.hdr = {
1192d4afb5ceSopenharmony_ci		"lws OpenMetrics export",
1193d4afb5ceSopenharmony_ci		"lws_protocol_plugin",
1194d4afb5ceSopenharmony_ci		LWS_BUILD_HASH,
1195d4afb5ceSopenharmony_ci		LWS_PLUGIN_API_MAGIC
1196d4afb5ceSopenharmony_ci	},
1197d4afb5ceSopenharmony_ci
1198d4afb5ceSopenharmony_ci	.protocols = lws_openmetrics_export_protocols,
1199d4afb5ceSopenharmony_ci	.count_protocols = LWS_ARRAY_SIZE(lws_openmetrics_export_protocols),
1200d4afb5ceSopenharmony_ci};
1201