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