1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * lws-minimal-secure-streams-binance
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Written in 2010-2021 by Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *                         Kutoga <kutoga@user.github.invalid>
6d4afb5ceSopenharmony_ci *
7d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0
8d4afb5ceSopenharmony_ci * Universal Public Domain Dedication.
9d4afb5ceSopenharmony_ci *
10d4afb5ceSopenharmony_ci * This demonstrates a Secure Streams implementation of a client that connects
11d4afb5ceSopenharmony_ci * to binance ws server efficiently.
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * Build lws with -DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITHOUT_EXTENSIONS=0
14d4afb5ceSopenharmony_ci *
15d4afb5ceSopenharmony_ci * "policy.json" contains all the information about endpoints, protocols and
16d4afb5ceSopenharmony_ci * connection validation, tagged by streamtype name.
17d4afb5ceSopenharmony_ci *
18d4afb5ceSopenharmony_ci * The example tries to load it from the cwd, it lives
19d4afb5ceSopenharmony_ci * in ./minimal-examples/secure-streams/minimal-secure-streams-binance dir, so
20d4afb5ceSopenharmony_ci * either run it from there, or copy the policy.json to your cwd.  It's also
21d4afb5ceSopenharmony_ci * possible to put the policy json in the code as a string and pass that at
22d4afb5ceSopenharmony_ci * context creation time.
23d4afb5ceSopenharmony_ci */
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci#include <libwebsockets.h>
26d4afb5ceSopenharmony_ci#include <string.h>
27d4afb5ceSopenharmony_ci#include <signal.h>
28d4afb5ceSopenharmony_ci#include <ctype.h>
29d4afb5ceSopenharmony_ci
30d4afb5ceSopenharmony_cistatic int interrupted;
31d4afb5ceSopenharmony_ci
32d4afb5ceSopenharmony_citypedef struct range {
33d4afb5ceSopenharmony_ci	uint64_t		sum;
34d4afb5ceSopenharmony_ci	uint64_t		lowest;
35d4afb5ceSopenharmony_ci	uint64_t		highest;
36d4afb5ceSopenharmony_ci
37d4afb5ceSopenharmony_ci	unsigned int		samples;
38d4afb5ceSopenharmony_ci} range_t;
39d4afb5ceSopenharmony_ci
40d4afb5ceSopenharmony_citypedef struct binance {
41d4afb5ceSopenharmony_ci	struct lws_ss_handle 	*ss;
42d4afb5ceSopenharmony_ci	void			*opaque_data;
43d4afb5ceSopenharmony_ci
44d4afb5ceSopenharmony_ci	lws_sorted_usec_list_t	sul_hz;	     /* 1hz summary dump */
45d4afb5ceSopenharmony_ci
46d4afb5ceSopenharmony_ci	range_t			e_lat_range;
47d4afb5ceSopenharmony_ci	range_t			price_range;
48d4afb5ceSopenharmony_ci} binance_t;
49d4afb5ceSopenharmony_ci
50d4afb5ceSopenharmony_ci/****** Part 1 / 3: application data processing */
51d4afb5ceSopenharmony_ci
52d4afb5ceSopenharmony_cistatic void
53d4afb5ceSopenharmony_cirange_reset(range_t *r)
54d4afb5ceSopenharmony_ci{
55d4afb5ceSopenharmony_ci	r->sum = r->highest = 0;
56d4afb5ceSopenharmony_ci	r->lowest = 999999999999ull;
57d4afb5ceSopenharmony_ci	r->samples = 0;
58d4afb5ceSopenharmony_ci}
59d4afb5ceSopenharmony_ci
60d4afb5ceSopenharmony_cistatic uint64_t
61d4afb5ceSopenharmony_ciget_us_timeofday(void)
62d4afb5ceSopenharmony_ci{
63d4afb5ceSopenharmony_ci	struct timeval tv;
64d4afb5ceSopenharmony_ci
65d4afb5ceSopenharmony_ci	gettimeofday(&tv, NULL);
66d4afb5ceSopenharmony_ci
67d4afb5ceSopenharmony_ci	return (uint64_t)((lws_usec_t)tv.tv_sec * LWS_US_PER_SEC) +
68d4afb5ceSopenharmony_ci			  (uint64_t)tv.tv_usec;
69d4afb5ceSopenharmony_ci}
70d4afb5ceSopenharmony_ci
71d4afb5ceSopenharmony_cistatic uint64_t
72d4afb5ceSopenharmony_cipennies(const char *s)
73d4afb5ceSopenharmony_ci{
74d4afb5ceSopenharmony_ci	uint64_t price = (uint64_t)atoll(s) * 100;
75d4afb5ceSopenharmony_ci
76d4afb5ceSopenharmony_ci	s = strchr(s, '.');
77d4afb5ceSopenharmony_ci
78d4afb5ceSopenharmony_ci	if (s && isdigit(s[1]) && isdigit(s[2]))
79d4afb5ceSopenharmony_ci		price = price + (uint64_t)((10 * (s[1] - '0')) + (s[2] - '0'));
80d4afb5ceSopenharmony_ci
81d4afb5ceSopenharmony_ci	return price;
82d4afb5ceSopenharmony_ci}
83d4afb5ceSopenharmony_ci
84d4afb5ceSopenharmony_cistatic void
85d4afb5ceSopenharmony_cisul_hz_cb(lws_sorted_usec_list_t *sul)
86d4afb5ceSopenharmony_ci{
87d4afb5ceSopenharmony_ci	binance_t *bin = lws_container_of(sul, binance_t, sul_hz);
88d4afb5ceSopenharmony_ci
89d4afb5ceSopenharmony_ci	/*
90d4afb5ceSopenharmony_ci	 * We are called once a second to dump statistics on the connection
91d4afb5ceSopenharmony_ci	 */
92d4afb5ceSopenharmony_ci
93d4afb5ceSopenharmony_ci	lws_sul_schedule(lws_ss_get_context(bin->ss), 0, &bin->sul_hz,
94d4afb5ceSopenharmony_ci			 sul_hz_cb, LWS_US_PER_SEC);
95d4afb5ceSopenharmony_ci
96d4afb5ceSopenharmony_ci	if (bin->price_range.samples)
97d4afb5ceSopenharmony_ci		lwsl_notice("%s: price: min: %llu¢, max: %llu¢, avg: %llu¢, "
98d4afb5ceSopenharmony_ci			    "(%d prices/s)\n", __func__,
99d4afb5ceSopenharmony_ci			    (unsigned long long)bin->price_range.lowest,
100d4afb5ceSopenharmony_ci			    (unsigned long long)bin->price_range.highest,
101d4afb5ceSopenharmony_ci			    (unsigned long long)(bin->price_range.sum /
102d4afb5ceSopenharmony_ci						    bin->price_range.samples),
103d4afb5ceSopenharmony_ci			    bin->price_range.samples);
104d4afb5ceSopenharmony_ci	if (bin->e_lat_range.samples)
105d4afb5ceSopenharmony_ci		lwsl_notice("%s: elatency: min: %llums, max: %llums, "
106d4afb5ceSopenharmony_ci			    "avg: %llums, (%d msg/s)\n", __func__,
107d4afb5ceSopenharmony_ci			    (unsigned long long)bin->e_lat_range.lowest / 1000,
108d4afb5ceSopenharmony_ci			    (unsigned long long)bin->e_lat_range.highest / 1000,
109d4afb5ceSopenharmony_ci			    (unsigned long long)(bin->e_lat_range.sum /
110d4afb5ceSopenharmony_ci					   bin->e_lat_range.samples) / 1000,
111d4afb5ceSopenharmony_ci			    bin->e_lat_range.samples);
112d4afb5ceSopenharmony_ci
113d4afb5ceSopenharmony_ci	range_reset(&bin->e_lat_range);
114d4afb5ceSopenharmony_ci	range_reset(&bin->price_range);
115d4afb5ceSopenharmony_ci}
116d4afb5ceSopenharmony_ci
117d4afb5ceSopenharmony_ci/****** Part 2 / 3: communication */
118d4afb5ceSopenharmony_ci
119d4afb5ceSopenharmony_cistatic lws_ss_state_return_t
120d4afb5ceSopenharmony_cibinance_rx(void *userobj, const uint8_t *in, size_t len, int flags)
121d4afb5ceSopenharmony_ci{
122d4afb5ceSopenharmony_ci	binance_t *bin = (binance_t *)userobj;
123d4afb5ceSopenharmony_ci	uint64_t latency_us, now_us;
124d4afb5ceSopenharmony_ci	char numbuf[16];
125d4afb5ceSopenharmony_ci	uint64_t price;
126d4afb5ceSopenharmony_ci	const char *p;
127d4afb5ceSopenharmony_ci	size_t alen;
128d4afb5ceSopenharmony_ci
129d4afb5ceSopenharmony_ci	now_us = (uint64_t)get_us_timeofday();
130d4afb5ceSopenharmony_ci
131d4afb5ceSopenharmony_ci	p = lws_json_simple_find((const char *)in, len, "\"depthUpdate\"",
132d4afb5ceSopenharmony_ci				 &alen);
133d4afb5ceSopenharmony_ci	if (!p)
134d4afb5ceSopenharmony_ci		return LWSSSSRET_OK;
135d4afb5ceSopenharmony_ci
136d4afb5ceSopenharmony_ci	p = lws_json_simple_find((const char *)in, len, "\"E\":", &alen);
137d4afb5ceSopenharmony_ci	if (!p) {
138d4afb5ceSopenharmony_ci		lwsl_err("%s: no E JSON\n", __func__);
139d4afb5ceSopenharmony_ci		return LWSSSSRET_OK;
140d4afb5ceSopenharmony_ci	}
141d4afb5ceSopenharmony_ci
142d4afb5ceSopenharmony_ci	lws_strnncpy(numbuf, p, alen, sizeof(numbuf));
143d4afb5ceSopenharmony_ci	latency_us = now_us - ((uint64_t)atoll(numbuf) * LWS_US_PER_MS);
144d4afb5ceSopenharmony_ci
145d4afb5ceSopenharmony_ci	if (latency_us < bin->e_lat_range.lowest)
146d4afb5ceSopenharmony_ci		bin->e_lat_range.lowest = latency_us;
147d4afb5ceSopenharmony_ci	if (latency_us > bin->e_lat_range.highest)
148d4afb5ceSopenharmony_ci		bin->e_lat_range.highest = latency_us;
149d4afb5ceSopenharmony_ci
150d4afb5ceSopenharmony_ci	bin->e_lat_range.sum += latency_us;
151d4afb5ceSopenharmony_ci	bin->e_lat_range.samples++;
152d4afb5ceSopenharmony_ci
153d4afb5ceSopenharmony_ci	p = lws_json_simple_find((const char *)in, len, "\"a\":[[\"", &alen);
154d4afb5ceSopenharmony_ci	if (!p)
155d4afb5ceSopenharmony_ci		return LWSSSSRET_OK;
156d4afb5ceSopenharmony_ci
157d4afb5ceSopenharmony_ci	lws_strnncpy(numbuf, p, alen, sizeof(numbuf));
158d4afb5ceSopenharmony_ci	price = pennies(numbuf);
159d4afb5ceSopenharmony_ci
160d4afb5ceSopenharmony_ci	if (price < bin->price_range.lowest)
161d4afb5ceSopenharmony_ci		bin->price_range.lowest = price;
162d4afb5ceSopenharmony_ci	if (price > bin->price_range.highest)
163d4afb5ceSopenharmony_ci		bin->price_range.highest = price;
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ci	bin->price_range.sum += price;
166d4afb5ceSopenharmony_ci	bin->price_range.samples++;
167d4afb5ceSopenharmony_ci
168d4afb5ceSopenharmony_ci	return LWSSSSRET_OK;
169d4afb5ceSopenharmony_ci}
170d4afb5ceSopenharmony_ci
171d4afb5ceSopenharmony_cistatic lws_ss_state_return_t
172d4afb5ceSopenharmony_cibinance_state(void *userobj, void *h_src, lws_ss_constate_t state,
173d4afb5ceSopenharmony_ci	      lws_ss_tx_ordinal_t ack)
174d4afb5ceSopenharmony_ci{
175d4afb5ceSopenharmony_ci	binance_t *bin = (binance_t *)userobj;
176d4afb5ceSopenharmony_ci
177d4afb5ceSopenharmony_ci	lwsl_ss_info(bin->ss, "%s (%d), ord 0x%x",
178d4afb5ceSopenharmony_ci		     lws_ss_state_name((int)state), state, (unsigned int)ack);
179d4afb5ceSopenharmony_ci
180d4afb5ceSopenharmony_ci	switch (state) {
181d4afb5ceSopenharmony_ci
182d4afb5ceSopenharmony_ci	case LWSSSCS_CONNECTED:
183d4afb5ceSopenharmony_ci		lws_sul_schedule(lws_ss_get_context(bin->ss), 0, &bin->sul_hz,
184d4afb5ceSopenharmony_ci				 sul_hz_cb, LWS_US_PER_SEC);
185d4afb5ceSopenharmony_ci		range_reset(&bin->e_lat_range);
186d4afb5ceSopenharmony_ci		range_reset(&bin->price_range);
187d4afb5ceSopenharmony_ci
188d4afb5ceSopenharmony_ci		return LWSSSSRET_OK;
189d4afb5ceSopenharmony_ci
190d4afb5ceSopenharmony_ci	case LWSSSCS_DISCONNECTED:
191d4afb5ceSopenharmony_ci		lws_sul_cancel(&bin->sul_hz);
192d4afb5ceSopenharmony_ci		break;
193d4afb5ceSopenharmony_ci
194d4afb5ceSopenharmony_ci	default:
195d4afb5ceSopenharmony_ci		break;
196d4afb5ceSopenharmony_ci	}
197d4afb5ceSopenharmony_ci
198d4afb5ceSopenharmony_ci	return LWSSSSRET_OK;
199d4afb5ceSopenharmony_ci}
200d4afb5ceSopenharmony_ci
201d4afb5ceSopenharmony_cistatic const lws_ss_info_t ssi_binance = {
202d4afb5ceSopenharmony_ci	.handle_offset		  = offsetof(binance_t, ss),
203d4afb5ceSopenharmony_ci	.opaque_user_data_offset  = offsetof(binance_t, opaque_data),
204d4afb5ceSopenharmony_ci	.rx			  = binance_rx,
205d4afb5ceSopenharmony_ci	.state			  = binance_state,
206d4afb5ceSopenharmony_ci	.user_alloc		  = sizeof(binance_t),
207d4afb5ceSopenharmony_ci	.streamtype		  = "binance", /* bind to corresponding policy */
208d4afb5ceSopenharmony_ci};
209d4afb5ceSopenharmony_ci
210d4afb5ceSopenharmony_ci/****** Part 3 / 3: init and event loop */
211d4afb5ceSopenharmony_ci
212d4afb5ceSopenharmony_cistatic const struct lws_extension extensions[] = {
213d4afb5ceSopenharmony_ci	{
214d4afb5ceSopenharmony_ci		"permessage-deflate", lws_extension_callback_pm_deflate,
215d4afb5ceSopenharmony_ci		"permessage-deflate" "; client_no_context_takeover"
216d4afb5ceSopenharmony_ci		 "; client_max_window_bits"
217d4afb5ceSopenharmony_ci	},
218d4afb5ceSopenharmony_ci	{ NULL, NULL, NULL /* terminator */ }
219d4afb5ceSopenharmony_ci};
220d4afb5ceSopenharmony_ci
221d4afb5ceSopenharmony_cistatic void
222d4afb5ceSopenharmony_cisigint_handler(int sig)
223d4afb5ceSopenharmony_ci{
224d4afb5ceSopenharmony_ci	interrupted = 1;
225d4afb5ceSopenharmony_ci}
226d4afb5ceSopenharmony_ci
227d4afb5ceSopenharmony_ciint main(int argc, const char **argv)
228d4afb5ceSopenharmony_ci{
229d4afb5ceSopenharmony_ci	struct lws_context_creation_info info;
230d4afb5ceSopenharmony_ci	struct lws_context *cx;
231d4afb5ceSopenharmony_ci	int n = 0;
232d4afb5ceSopenharmony_ci
233d4afb5ceSopenharmony_ci	signal(SIGINT, sigint_handler);
234d4afb5ceSopenharmony_ci
235d4afb5ceSopenharmony_ci	memset(&info, 0, sizeof info);
236d4afb5ceSopenharmony_ci	lws_cmdline_option_handle_builtin(argc, argv, &info);
237d4afb5ceSopenharmony_ci
238d4afb5ceSopenharmony_ci	lwsl_user("LWS minimal Secure Streams binance client\n");
239d4afb5ceSopenharmony_ci
240d4afb5ceSopenharmony_ci	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
241d4afb5ceSopenharmony_ci		       LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
242d4afb5ceSopenharmony_ci	info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
243d4afb5ceSopenharmony_ci	info.fd_limit_per_thread = 1 + 1 + 1;
244d4afb5ceSopenharmony_ci	info.extensions = extensions;
245d4afb5ceSopenharmony_ci	info.pss_policies_json = "policy.json"; /* literal JSON, or path */
246d4afb5ceSopenharmony_ci
247d4afb5ceSopenharmony_ci	cx = lws_create_context(&info);
248d4afb5ceSopenharmony_ci	if (!cx) {
249d4afb5ceSopenharmony_ci		lwsl_err("lws init failed\n");
250d4afb5ceSopenharmony_ci		return 1;
251d4afb5ceSopenharmony_ci	}
252d4afb5ceSopenharmony_ci
253d4afb5ceSopenharmony_ci	if (lws_ss_create(cx, 0, &ssi_binance, NULL, NULL, NULL, NULL)) {
254d4afb5ceSopenharmony_ci		lwsl_cx_err(cx, "failed to create secure stream");
255d4afb5ceSopenharmony_ci		interrupted = 1;
256d4afb5ceSopenharmony_ci	}
257d4afb5ceSopenharmony_ci
258d4afb5ceSopenharmony_ci	while (n >= 0 && !interrupted)
259d4afb5ceSopenharmony_ci		n = lws_service(cx, 0);
260d4afb5ceSopenharmony_ci
261d4afb5ceSopenharmony_ci	lws_context_destroy(cx);
262d4afb5ceSopenharmony_ci
263d4afb5ceSopenharmony_ci	lwsl_user("Completed\n");
264d4afb5ceSopenharmony_ci
265d4afb5ceSopenharmony_ci	return 0;
266d4afb5ceSopenharmony_ci}
267