1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * ws protocol handler plugin for "lws-minimal" 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Written in 2010-2019 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 version uses an lws_ring ringbuffer to cache up to 8 messages at a time, 10d4afb5ceSopenharmony_ci * so it's not so easy to lose messages. 11d4afb5ceSopenharmony_ci * 12d4afb5ceSopenharmony_ci * This also demonstrates how to "cull", ie, kill, connections that can't 13d4afb5ceSopenharmony_ci * keep up for some reason. 14d4afb5ceSopenharmony_ci */ 15d4afb5ceSopenharmony_ci 16d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC) 17d4afb5ceSopenharmony_ci#define LWS_DLL 18d4afb5ceSopenharmony_ci#define LWS_INTERNAL 19d4afb5ceSopenharmony_ci#include <libwebsockets.h> 20d4afb5ceSopenharmony_ci#endif 21d4afb5ceSopenharmony_ci 22d4afb5ceSopenharmony_ci#include <string.h> 23d4afb5ceSopenharmony_ci 24d4afb5ceSopenharmony_ci/* one of these created for each message */ 25d4afb5ceSopenharmony_ci 26d4afb5ceSopenharmony_cistruct msg { 27d4afb5ceSopenharmony_ci void *payload; /* is malloc'd */ 28d4afb5ceSopenharmony_ci size_t len; 29d4afb5ceSopenharmony_ci}; 30d4afb5ceSopenharmony_ci 31d4afb5ceSopenharmony_ci/* one of these is created for each client connecting to us */ 32d4afb5ceSopenharmony_ci 33d4afb5ceSopenharmony_cistruct per_session_data__minimal { 34d4afb5ceSopenharmony_ci struct per_session_data__minimal *pss_list; 35d4afb5ceSopenharmony_ci struct lws *wsi; 36d4afb5ceSopenharmony_ci uint32_t tail; 37d4afb5ceSopenharmony_ci 38d4afb5ceSopenharmony_ci unsigned int culled:1; 39d4afb5ceSopenharmony_ci}; 40d4afb5ceSopenharmony_ci 41d4afb5ceSopenharmony_ci/* one of these is created for each vhost our protocol is used with */ 42d4afb5ceSopenharmony_ci 43d4afb5ceSopenharmony_cistruct per_vhost_data__minimal { 44d4afb5ceSopenharmony_ci struct lws_context *context; 45d4afb5ceSopenharmony_ci struct lws_vhost *vhost; 46d4afb5ceSopenharmony_ci const struct lws_protocols *protocol; 47d4afb5ceSopenharmony_ci 48d4afb5ceSopenharmony_ci struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ 49d4afb5ceSopenharmony_ci 50d4afb5ceSopenharmony_ci struct lws_ring *ring; /* ringbuffer holding unsent messages */ 51d4afb5ceSopenharmony_ci}; 52d4afb5ceSopenharmony_ci 53d4afb5ceSopenharmony_cistatic void 54d4afb5ceSopenharmony_cicull_lagging_clients(struct per_vhost_data__minimal *vhd) 55d4afb5ceSopenharmony_ci{ 56d4afb5ceSopenharmony_ci uint32_t oldest_tail = lws_ring_get_oldest_tail(vhd->ring); 57d4afb5ceSopenharmony_ci struct per_session_data__minimal *old_pss = NULL; 58d4afb5ceSopenharmony_ci int most = 0, before = (int)lws_ring_get_count_waiting_elements(vhd->ring, 59d4afb5ceSopenharmony_ci &oldest_tail), m; 60d4afb5ceSopenharmony_ci 61d4afb5ceSopenharmony_ci /* 62d4afb5ceSopenharmony_ci * At least one guy with the oldest tail has lagged too far, filling 63d4afb5ceSopenharmony_ci * the ringbuffer with stuff waiting for them, while new stuff is 64d4afb5ceSopenharmony_ci * coming in, and they must close, freeing up ringbuffer entries. 65d4afb5ceSopenharmony_ci */ 66d4afb5ceSopenharmony_ci 67d4afb5ceSopenharmony_ci lws_start_foreach_llp_safe(struct per_session_data__minimal **, 68d4afb5ceSopenharmony_ci ppss, vhd->pss_list, pss_list) { 69d4afb5ceSopenharmony_ci 70d4afb5ceSopenharmony_ci if ((*ppss)->tail == oldest_tail) { 71d4afb5ceSopenharmony_ci old_pss = *ppss; 72d4afb5ceSopenharmony_ci 73d4afb5ceSopenharmony_ci lwsl_user("Killing lagging client %p\n", (*ppss)->wsi); 74d4afb5ceSopenharmony_ci 75d4afb5ceSopenharmony_ci lws_set_timeout((*ppss)->wsi, PENDING_TIMEOUT_LAGGING, 76d4afb5ceSopenharmony_ci /* 77d4afb5ceSopenharmony_ci * we may kill the wsi we came in on, 78d4afb5ceSopenharmony_ci * so the actual close is deferred 79d4afb5ceSopenharmony_ci */ 80d4afb5ceSopenharmony_ci LWS_TO_KILL_ASYNC); 81d4afb5ceSopenharmony_ci 82d4afb5ceSopenharmony_ci /* 83d4afb5ceSopenharmony_ci * We might try to write something before we get a 84d4afb5ceSopenharmony_ci * chance to close. But this pss is now detached 85d4afb5ceSopenharmony_ci * from the ring buffer. Mark this pss as culled so we 86d4afb5ceSopenharmony_ci * don't try to do anything more with it. 87d4afb5ceSopenharmony_ci */ 88d4afb5ceSopenharmony_ci 89d4afb5ceSopenharmony_ci (*ppss)->culled = 1; 90d4afb5ceSopenharmony_ci 91d4afb5ceSopenharmony_ci /* 92d4afb5ceSopenharmony_ci * Because we can't kill it synchronously, but we 93d4afb5ceSopenharmony_ci * know it's closing momentarily and don't want its 94d4afb5ceSopenharmony_ci * participation any more, remove its pss from the 95d4afb5ceSopenharmony_ci * vhd pss list early. (This is safe to repeat 96d4afb5ceSopenharmony_ci * uselessly later in the close flow). 97d4afb5ceSopenharmony_ci * 98d4afb5ceSopenharmony_ci * Notice this changes *ppss! 99d4afb5ceSopenharmony_ci */ 100d4afb5ceSopenharmony_ci 101d4afb5ceSopenharmony_ci lws_ll_fwd_remove(struct per_session_data__minimal, 102d4afb5ceSopenharmony_ci pss_list, (*ppss), vhd->pss_list); 103d4afb5ceSopenharmony_ci 104d4afb5ceSopenharmony_ci /* use the changed *ppss so we won't skip anything */ 105d4afb5ceSopenharmony_ci 106d4afb5ceSopenharmony_ci continue; 107d4afb5ceSopenharmony_ci 108d4afb5ceSopenharmony_ci } else { 109d4afb5ceSopenharmony_ci /* 110d4afb5ceSopenharmony_ci * so this guy is a survivor of the cull. Let's track 111d4afb5ceSopenharmony_ci * what is the largest number of pending ring elements 112d4afb5ceSopenharmony_ci * for any survivor. 113d4afb5ceSopenharmony_ci */ 114d4afb5ceSopenharmony_ci m = (int)lws_ring_get_count_waiting_elements(vhd->ring, 115d4afb5ceSopenharmony_ci &((*ppss)->tail)); 116d4afb5ceSopenharmony_ci if (m > most) 117d4afb5ceSopenharmony_ci most = m; 118d4afb5ceSopenharmony_ci } 119d4afb5ceSopenharmony_ci 120d4afb5ceSopenharmony_ci } lws_end_foreach_llp_safe(ppss); 121d4afb5ceSopenharmony_ci 122d4afb5ceSopenharmony_ci /* it would mean we lost track of oldest... but Coverity insists */ 123d4afb5ceSopenharmony_ci if (!old_pss) 124d4afb5ceSopenharmony_ci return; 125d4afb5ceSopenharmony_ci 126d4afb5ceSopenharmony_ci /* 127d4afb5ceSopenharmony_ci * Let's recover (ie, free up) all the ring slots between the 128d4afb5ceSopenharmony_ci * original oldest's last one and the "worst" survivor. 129d4afb5ceSopenharmony_ci */ 130d4afb5ceSopenharmony_ci 131d4afb5ceSopenharmony_ci lws_ring_consume_and_update_oldest_tail(vhd->ring, 132d4afb5ceSopenharmony_ci struct per_session_data__minimal, &old_pss->tail, (size_t)(before - most), 133d4afb5ceSopenharmony_ci vhd->pss_list, tail, pss_list); 134d4afb5ceSopenharmony_ci 135d4afb5ceSopenharmony_ci lwsl_user("%s: shrunk ring from %d to %d\n", __func__, before, most); 136d4afb5ceSopenharmony_ci} 137d4afb5ceSopenharmony_ci 138d4afb5ceSopenharmony_ci/* destroys the message when everyone has had a copy of it */ 139d4afb5ceSopenharmony_ci 140d4afb5ceSopenharmony_cistatic void 141d4afb5ceSopenharmony_ci__minimal_destroy_message(void *_msg) 142d4afb5ceSopenharmony_ci{ 143d4afb5ceSopenharmony_ci struct msg *msg = _msg; 144d4afb5ceSopenharmony_ci 145d4afb5ceSopenharmony_ci free(msg->payload); 146d4afb5ceSopenharmony_ci msg->payload = NULL; 147d4afb5ceSopenharmony_ci msg->len = 0; 148d4afb5ceSopenharmony_ci} 149d4afb5ceSopenharmony_ci 150d4afb5ceSopenharmony_cistatic int 151d4afb5ceSopenharmony_cicallback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 152d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 153d4afb5ceSopenharmony_ci{ 154d4afb5ceSopenharmony_ci struct per_session_data__minimal *pss = 155d4afb5ceSopenharmony_ci (struct per_session_data__minimal *)user; 156d4afb5ceSopenharmony_ci struct per_vhost_data__minimal *vhd = 157d4afb5ceSopenharmony_ci (struct per_vhost_data__minimal *) 158d4afb5ceSopenharmony_ci lws_protocol_vh_priv_get(lws_get_vhost(wsi), 159d4afb5ceSopenharmony_ci lws_get_protocol(wsi)); 160d4afb5ceSopenharmony_ci const struct msg *pmsg; 161d4afb5ceSopenharmony_ci struct msg amsg; 162d4afb5ceSopenharmony_ci int n, m; 163d4afb5ceSopenharmony_ci 164d4afb5ceSopenharmony_ci switch (reason) { 165d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_INIT: 166d4afb5ceSopenharmony_ci vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), 167d4afb5ceSopenharmony_ci lws_get_protocol(wsi), 168d4afb5ceSopenharmony_ci sizeof(struct per_vhost_data__minimal)); 169d4afb5ceSopenharmony_ci vhd->context = lws_get_context(wsi); 170d4afb5ceSopenharmony_ci vhd->protocol = lws_get_protocol(wsi); 171d4afb5ceSopenharmony_ci vhd->vhost = lws_get_vhost(wsi); 172d4afb5ceSopenharmony_ci 173d4afb5ceSopenharmony_ci vhd->ring = lws_ring_create(sizeof(struct msg), 8, 174d4afb5ceSopenharmony_ci __minimal_destroy_message); 175d4afb5ceSopenharmony_ci if (!vhd->ring) 176d4afb5ceSopenharmony_ci return 1; 177d4afb5ceSopenharmony_ci break; 178d4afb5ceSopenharmony_ci 179d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_DESTROY: 180d4afb5ceSopenharmony_ci lws_ring_destroy(vhd->ring); 181d4afb5ceSopenharmony_ci break; 182d4afb5ceSopenharmony_ci 183d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED: 184d4afb5ceSopenharmony_ci /* add ourselves to the list of live pss held in the vhd */ 185d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_ESTABLISHED: wsi %p\n", wsi); 186d4afb5ceSopenharmony_ci lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); 187d4afb5ceSopenharmony_ci pss->tail = lws_ring_get_oldest_tail(vhd->ring); 188d4afb5ceSopenharmony_ci pss->wsi = wsi; 189d4afb5ceSopenharmony_ci break; 190d4afb5ceSopenharmony_ci 191d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED: 192d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_CLOSED: wsi %p\n", wsi); 193d4afb5ceSopenharmony_ci /* remove our closing pss from the list of live pss */ 194d4afb5ceSopenharmony_ci lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, 195d4afb5ceSopenharmony_ci pss, vhd->pss_list); 196d4afb5ceSopenharmony_ci break; 197d4afb5ceSopenharmony_ci 198d4afb5ceSopenharmony_ci case LWS_CALLBACK_SERVER_WRITEABLE: 199d4afb5ceSopenharmony_ci if (pss->culled) 200d4afb5ceSopenharmony_ci break; 201d4afb5ceSopenharmony_ci pmsg = lws_ring_get_element(vhd->ring, &pss->tail); 202d4afb5ceSopenharmony_ci if (!pmsg) 203d4afb5ceSopenharmony_ci break; 204d4afb5ceSopenharmony_ci 205d4afb5ceSopenharmony_ci /* notice we allowed for LWS_PRE in the payload already */ 206d4afb5ceSopenharmony_ci m = lws_write(wsi, ((unsigned char *)pmsg->payload) + 207d4afb5ceSopenharmony_ci LWS_PRE, pmsg->len, LWS_WRITE_TEXT); 208d4afb5ceSopenharmony_ci if (m < (int)pmsg->len) { 209d4afb5ceSopenharmony_ci lwsl_err("ERROR %d writing to ws socket\n", m); 210d4afb5ceSopenharmony_ci return -1; 211d4afb5ceSopenharmony_ci } 212d4afb5ceSopenharmony_ci 213d4afb5ceSopenharmony_ci lws_ring_consume_and_update_oldest_tail( 214d4afb5ceSopenharmony_ci vhd->ring, /* lws_ring object */ 215d4afb5ceSopenharmony_ci struct per_session_data__minimal, /* type of objects with tails */ 216d4afb5ceSopenharmony_ci &pss->tail, /* tail of guy doing the consuming */ 217d4afb5ceSopenharmony_ci 1, /* number of payload objects being consumed */ 218d4afb5ceSopenharmony_ci vhd->pss_list, /* head of list of objects with tails */ 219d4afb5ceSopenharmony_ci tail, /* member name of tail in objects with tails */ 220d4afb5ceSopenharmony_ci pss_list /* member name of next object in objects with tails */ 221d4afb5ceSopenharmony_ci ); 222d4afb5ceSopenharmony_ci 223d4afb5ceSopenharmony_ci /* more to do for us? */ 224d4afb5ceSopenharmony_ci if (lws_ring_get_element(vhd->ring, &pss->tail)) 225d4afb5ceSopenharmony_ci /* come back as soon as we can write more */ 226d4afb5ceSopenharmony_ci lws_callback_on_writable(pss->wsi); 227d4afb5ceSopenharmony_ci break; 228d4afb5ceSopenharmony_ci 229d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE: 230d4afb5ceSopenharmony_ci n = (int)lws_ring_get_count_free_elements(vhd->ring); 231d4afb5ceSopenharmony_ci if (!n) { 232d4afb5ceSopenharmony_ci /* forcibly make space */ 233d4afb5ceSopenharmony_ci cull_lagging_clients(vhd); 234d4afb5ceSopenharmony_ci n = (int)lws_ring_get_count_free_elements(vhd->ring); 235d4afb5ceSopenharmony_ci } 236d4afb5ceSopenharmony_ci if (!n) 237d4afb5ceSopenharmony_ci break; 238d4afb5ceSopenharmony_ci 239d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_RECEIVE: free space %d\n", n); 240d4afb5ceSopenharmony_ci 241d4afb5ceSopenharmony_ci amsg.len = len; 242d4afb5ceSopenharmony_ci /* notice we over-allocate by LWS_PRE... */ 243d4afb5ceSopenharmony_ci amsg.payload = malloc(LWS_PRE + len); 244d4afb5ceSopenharmony_ci if (!amsg.payload) { 245d4afb5ceSopenharmony_ci lwsl_user("OOM: dropping\n"); 246d4afb5ceSopenharmony_ci break; 247d4afb5ceSopenharmony_ci } 248d4afb5ceSopenharmony_ci 249d4afb5ceSopenharmony_ci /* ...and we copy the payload in at +LWS_PRE */ 250d4afb5ceSopenharmony_ci memcpy((char *)amsg.payload + LWS_PRE, in, len); 251d4afb5ceSopenharmony_ci if (!lws_ring_insert(vhd->ring, &amsg, 1)) { 252d4afb5ceSopenharmony_ci __minimal_destroy_message(&amsg); 253d4afb5ceSopenharmony_ci lwsl_user("dropping!\n"); 254d4afb5ceSopenharmony_ci break; 255d4afb5ceSopenharmony_ci } 256d4afb5ceSopenharmony_ci 257d4afb5ceSopenharmony_ci /* 258d4afb5ceSopenharmony_ci * let everybody know we want to write something on them 259d4afb5ceSopenharmony_ci * as soon as they are ready 260d4afb5ceSopenharmony_ci */ 261d4afb5ceSopenharmony_ci lws_start_foreach_llp(struct per_session_data__minimal **, 262d4afb5ceSopenharmony_ci ppss, vhd->pss_list) { 263d4afb5ceSopenharmony_ci lws_callback_on_writable((*ppss)->wsi); 264d4afb5ceSopenharmony_ci } lws_end_foreach_llp(ppss, pss_list); 265d4afb5ceSopenharmony_ci break; 266d4afb5ceSopenharmony_ci 267d4afb5ceSopenharmony_ci default: 268d4afb5ceSopenharmony_ci break; 269d4afb5ceSopenharmony_ci } 270d4afb5ceSopenharmony_ci 271d4afb5ceSopenharmony_ci return 0; 272d4afb5ceSopenharmony_ci} 273d4afb5ceSopenharmony_ci 274d4afb5ceSopenharmony_ci#define LWS_PLUGIN_PROTOCOL_MINIMAL \ 275d4afb5ceSopenharmony_ci { \ 276d4afb5ceSopenharmony_ci "lws-minimal", \ 277d4afb5ceSopenharmony_ci callback_minimal, \ 278d4afb5ceSopenharmony_ci sizeof(struct per_session_data__minimal), \ 279d4afb5ceSopenharmony_ci 0, \ 280d4afb5ceSopenharmony_ci 0, NULL, 0 \ 281d4afb5ceSopenharmony_ci } 282