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 13d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC) 14d4afb5ceSopenharmony_ci#define LWS_DLL 15d4afb5ceSopenharmony_ci#define LWS_INTERNAL 16d4afb5ceSopenharmony_ci#include <libwebsockets.h> 17d4afb5ceSopenharmony_ci#endif 18d4afb5ceSopenharmony_ci 19d4afb5ceSopenharmony_ci#include <string.h> 20d4afb5ceSopenharmony_ci#include <stdlib.h> 21d4afb5ceSopenharmony_ci 22d4afb5ceSopenharmony_ci/* one of these created for each message */ 23d4afb5ceSopenharmony_ci 24d4afb5ceSopenharmony_cistruct msg { 25d4afb5ceSopenharmony_ci void *payload; /* is malloc'd */ 26d4afb5ceSopenharmony_ci size_t len; 27d4afb5ceSopenharmony_ci}; 28d4afb5ceSopenharmony_ci 29d4afb5ceSopenharmony_ci/* one of these is created for each client connecting to us */ 30d4afb5ceSopenharmony_ci 31d4afb5ceSopenharmony_cistruct per_session_data__minimal { 32d4afb5ceSopenharmony_ci struct per_session_data__minimal *pss_list; 33d4afb5ceSopenharmony_ci struct lws *wsi; 34d4afb5ceSopenharmony_ci uint32_t tail; 35d4afb5ceSopenharmony_ci}; 36d4afb5ceSopenharmony_ci 37d4afb5ceSopenharmony_ci/* one of these is created for each vhost our protocol is used with */ 38d4afb5ceSopenharmony_ci 39d4afb5ceSopenharmony_cistruct per_vhost_data__minimal { 40d4afb5ceSopenharmony_ci struct lws_context *context; 41d4afb5ceSopenharmony_ci struct lws_vhost *vhost; 42d4afb5ceSopenharmony_ci const struct lws_protocols *protocol; 43d4afb5ceSopenharmony_ci 44d4afb5ceSopenharmony_ci lws_sorted_usec_list_t sul; 45d4afb5ceSopenharmony_ci 46d4afb5ceSopenharmony_ci struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ 47d4afb5ceSopenharmony_ci 48d4afb5ceSopenharmony_ci struct lws_ring *ring; /* ringbuffer holding unsent messages */ 49d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 50d4afb5ceSopenharmony_ci struct lws *client_wsi; 51d4afb5ceSopenharmony_ci}; 52d4afb5ceSopenharmony_ci 53d4afb5ceSopenharmony_ci/* destroys the message when everyone has had a copy of it */ 54d4afb5ceSopenharmony_ci 55d4afb5ceSopenharmony_cistatic void 56d4afb5ceSopenharmony_ci__minimal_destroy_message(void *_msg) 57d4afb5ceSopenharmony_ci{ 58d4afb5ceSopenharmony_ci struct msg *msg = _msg; 59d4afb5ceSopenharmony_ci 60d4afb5ceSopenharmony_ci free(msg->payload); 61d4afb5ceSopenharmony_ci msg->payload = NULL; 62d4afb5ceSopenharmony_ci msg->len = 0; 63d4afb5ceSopenharmony_ci} 64d4afb5ceSopenharmony_ci 65d4afb5ceSopenharmony_cistatic void 66d4afb5ceSopenharmony_cisul_connect_attempt(struct lws_sorted_usec_list *sul) 67d4afb5ceSopenharmony_ci{ 68d4afb5ceSopenharmony_ci struct per_vhost_data__minimal *vhd = 69d4afb5ceSopenharmony_ci lws_container_of(sul, struct per_vhost_data__minimal, sul); 70d4afb5ceSopenharmony_ci 71d4afb5ceSopenharmony_ci vhd->i.context = vhd->context; 72d4afb5ceSopenharmony_ci vhd->i.port = 443; 73d4afb5ceSopenharmony_ci vhd->i.address = "libwebsockets.org"; 74d4afb5ceSopenharmony_ci vhd->i.path = "/"; 75d4afb5ceSopenharmony_ci vhd->i.host = vhd->i.address; 76d4afb5ceSopenharmony_ci vhd->i.origin = vhd->i.address; 77d4afb5ceSopenharmony_ci vhd->i.ssl_connection = 1; 78d4afb5ceSopenharmony_ci 79d4afb5ceSopenharmony_ci vhd->i.protocol = "dumb-increment-protocol"; 80d4afb5ceSopenharmony_ci vhd->i.local_protocol_name = "lws-minimal-proxy"; 81d4afb5ceSopenharmony_ci vhd->i.pwsi = &vhd->client_wsi; 82d4afb5ceSopenharmony_ci 83d4afb5ceSopenharmony_ci if (!lws_client_connect_via_info(&vhd->i)) 84d4afb5ceSopenharmony_ci lws_sul_schedule(vhd->context, 0, &vhd->sul, 85d4afb5ceSopenharmony_ci sul_connect_attempt, 10 * LWS_US_PER_SEC); 86d4afb5ceSopenharmony_ci} 87d4afb5ceSopenharmony_ci 88d4afb5ceSopenharmony_cistatic int 89d4afb5ceSopenharmony_cicallback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 90d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 91d4afb5ceSopenharmony_ci{ 92d4afb5ceSopenharmony_ci struct per_session_data__minimal *pss = 93d4afb5ceSopenharmony_ci (struct per_session_data__minimal *)user; 94d4afb5ceSopenharmony_ci struct per_vhost_data__minimal *vhd = 95d4afb5ceSopenharmony_ci (struct per_vhost_data__minimal *) 96d4afb5ceSopenharmony_ci lws_protocol_vh_priv_get(lws_get_vhost(wsi), 97d4afb5ceSopenharmony_ci lws_get_protocol(wsi)); 98d4afb5ceSopenharmony_ci const struct msg *pmsg; 99d4afb5ceSopenharmony_ci struct msg amsg; 100d4afb5ceSopenharmony_ci int m; 101d4afb5ceSopenharmony_ci 102d4afb5ceSopenharmony_ci switch (reason) { 103d4afb5ceSopenharmony_ci 104d4afb5ceSopenharmony_ci /* --- protocol lifecycle callbacks --- */ 105d4afb5ceSopenharmony_ci 106d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_INIT: 107d4afb5ceSopenharmony_ci vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), 108d4afb5ceSopenharmony_ci lws_get_protocol(wsi), 109d4afb5ceSopenharmony_ci sizeof(struct per_vhost_data__minimal)); 110d4afb5ceSopenharmony_ci vhd->context = lws_get_context(wsi); 111d4afb5ceSopenharmony_ci vhd->protocol = lws_get_protocol(wsi); 112d4afb5ceSopenharmony_ci vhd->vhost = lws_get_vhost(wsi); 113d4afb5ceSopenharmony_ci 114d4afb5ceSopenharmony_ci vhd->ring = lws_ring_create(sizeof(struct msg), 8, 115d4afb5ceSopenharmony_ci __minimal_destroy_message); 116d4afb5ceSopenharmony_ci if (!vhd->ring) 117d4afb5ceSopenharmony_ci return 1; 118d4afb5ceSopenharmony_ci 119d4afb5ceSopenharmony_ci sul_connect_attempt(&vhd->sul); 120d4afb5ceSopenharmony_ci break; 121d4afb5ceSopenharmony_ci 122d4afb5ceSopenharmony_ci case LWS_CALLBACK_PROTOCOL_DESTROY: 123d4afb5ceSopenharmony_ci lws_ring_destroy(vhd->ring); 124d4afb5ceSopenharmony_ci lws_sul_cancel(&vhd->sul); 125d4afb5ceSopenharmony_ci break; 126d4afb5ceSopenharmony_ci 127d4afb5ceSopenharmony_ci /* --- serving callbacks --- */ 128d4afb5ceSopenharmony_ci 129d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED: 130d4afb5ceSopenharmony_ci /* add ourselves to the list of live pss held in the vhd */ 131d4afb5ceSopenharmony_ci lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); 132d4afb5ceSopenharmony_ci pss->tail = lws_ring_get_oldest_tail(vhd->ring); 133d4afb5ceSopenharmony_ci pss->wsi = wsi; 134d4afb5ceSopenharmony_ci break; 135d4afb5ceSopenharmony_ci 136d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED: 137d4afb5ceSopenharmony_ci /* remove our closing pss from the list of live pss */ 138d4afb5ceSopenharmony_ci lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, 139d4afb5ceSopenharmony_ci pss, vhd->pss_list); 140d4afb5ceSopenharmony_ci break; 141d4afb5ceSopenharmony_ci 142d4afb5ceSopenharmony_ci case LWS_CALLBACK_SERVER_WRITEABLE: 143d4afb5ceSopenharmony_ci pmsg = lws_ring_get_element(vhd->ring, &pss->tail); 144d4afb5ceSopenharmony_ci if (!pmsg) 145d4afb5ceSopenharmony_ci break; 146d4afb5ceSopenharmony_ci 147d4afb5ceSopenharmony_ci /* notice we allowed for LWS_PRE in the payload already */ 148d4afb5ceSopenharmony_ci m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, 149d4afb5ceSopenharmony_ci pmsg->len, LWS_WRITE_TEXT); 150d4afb5ceSopenharmony_ci if (m < (int)pmsg->len) { 151d4afb5ceSopenharmony_ci lwsl_err("ERROR %d writing to ws socket\n", m); 152d4afb5ceSopenharmony_ci return -1; 153d4afb5ceSopenharmony_ci } 154d4afb5ceSopenharmony_ci 155d4afb5ceSopenharmony_ci lws_ring_consume_and_update_oldest_tail( 156d4afb5ceSopenharmony_ci vhd->ring, /* lws_ring object */ 157d4afb5ceSopenharmony_ci struct per_session_data__minimal, /* type of objects with tails */ 158d4afb5ceSopenharmony_ci &pss->tail, /* tail of guy doing the consuming */ 159d4afb5ceSopenharmony_ci 1, /* number of payload objects being consumed */ 160d4afb5ceSopenharmony_ci vhd->pss_list, /* head of list of objects with tails */ 161d4afb5ceSopenharmony_ci tail, /* member name of tail in objects with tails */ 162d4afb5ceSopenharmony_ci pss_list /* member name of next object in objects with tails */ 163d4afb5ceSopenharmony_ci ); 164d4afb5ceSopenharmony_ci 165d4afb5ceSopenharmony_ci /* more to do? */ 166d4afb5ceSopenharmony_ci if (lws_ring_get_element(vhd->ring, &pss->tail)) 167d4afb5ceSopenharmony_ci /* come back as soon as we can write more */ 168d4afb5ceSopenharmony_ci lws_callback_on_writable(pss->wsi); 169d4afb5ceSopenharmony_ci break; 170d4afb5ceSopenharmony_ci 171d4afb5ceSopenharmony_ci /* --- client callbacks --- */ 172d4afb5ceSopenharmony_ci 173d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 174d4afb5ceSopenharmony_ci lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", 175d4afb5ceSopenharmony_ci in ? (char *)in : "(null)"); 176d4afb5ceSopenharmony_ci vhd->client_wsi = NULL; 177d4afb5ceSopenharmony_ci lws_sul_schedule(vhd->context, 0, &vhd->sul, 178d4afb5ceSopenharmony_ci sul_connect_attempt, LWS_US_PER_SEC); 179d4afb5ceSopenharmony_ci break; 180d4afb5ceSopenharmony_ci 181d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_ESTABLISHED: 182d4afb5ceSopenharmony_ci lwsl_user("%s: established\n", __func__); 183d4afb5ceSopenharmony_ci break; 184d4afb5ceSopenharmony_ci 185d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_RECEIVE: 186d4afb5ceSopenharmony_ci /* if no clients, just drop incoming */ 187d4afb5ceSopenharmony_ci if (!vhd->pss_list) 188d4afb5ceSopenharmony_ci break; 189d4afb5ceSopenharmony_ci 190d4afb5ceSopenharmony_ci if (!lws_ring_get_count_free_elements(vhd->ring)) { 191d4afb5ceSopenharmony_ci lwsl_user("dropping!\n"); 192d4afb5ceSopenharmony_ci break; 193d4afb5ceSopenharmony_ci } 194d4afb5ceSopenharmony_ci 195d4afb5ceSopenharmony_ci amsg.len = len; 196d4afb5ceSopenharmony_ci /* notice we over-allocate by LWS_PRE */ 197d4afb5ceSopenharmony_ci amsg.payload = malloc(LWS_PRE + len); 198d4afb5ceSopenharmony_ci if (!amsg.payload) { 199d4afb5ceSopenharmony_ci lwsl_user("OOM: dropping\n"); 200d4afb5ceSopenharmony_ci break; 201d4afb5ceSopenharmony_ci } 202d4afb5ceSopenharmony_ci 203d4afb5ceSopenharmony_ci memcpy((char *)amsg.payload + LWS_PRE, in, len); 204d4afb5ceSopenharmony_ci if (!lws_ring_insert(vhd->ring, &amsg, 1)) { 205d4afb5ceSopenharmony_ci __minimal_destroy_message(&amsg); 206d4afb5ceSopenharmony_ci lwsl_user("dropping!\n"); 207d4afb5ceSopenharmony_ci break; 208d4afb5ceSopenharmony_ci } 209d4afb5ceSopenharmony_ci 210d4afb5ceSopenharmony_ci /* 211d4afb5ceSopenharmony_ci * let everybody know we want to write something on them 212d4afb5ceSopenharmony_ci * as soon as they are ready 213d4afb5ceSopenharmony_ci */ 214d4afb5ceSopenharmony_ci lws_start_foreach_llp(struct per_session_data__minimal **, 215d4afb5ceSopenharmony_ci ppss, vhd->pss_list) { 216d4afb5ceSopenharmony_ci lws_callback_on_writable((*ppss)->wsi); 217d4afb5ceSopenharmony_ci } lws_end_foreach_llp(ppss, pss_list); 218d4afb5ceSopenharmony_ci break; 219d4afb5ceSopenharmony_ci 220d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CLOSED: 221d4afb5ceSopenharmony_ci vhd->client_wsi = NULL; 222d4afb5ceSopenharmony_ci lws_sul_schedule(vhd->context, 0, &vhd->sul, 223d4afb5ceSopenharmony_ci sul_connect_attempt, LWS_US_PER_SEC); 224d4afb5ceSopenharmony_ci break; 225d4afb5ceSopenharmony_ci 226d4afb5ceSopenharmony_ci default: 227d4afb5ceSopenharmony_ci break; 228d4afb5ceSopenharmony_ci } 229d4afb5ceSopenharmony_ci 230d4afb5ceSopenharmony_ci return 0; 231d4afb5ceSopenharmony_ci} 232d4afb5ceSopenharmony_ci 233d4afb5ceSopenharmony_ci#define LWS_PLUGIN_PROTOCOL_MINIMAL \ 234d4afb5ceSopenharmony_ci { \ 235d4afb5ceSopenharmony_ci "lws-minimal-proxy", \ 236d4afb5ceSopenharmony_ci callback_minimal, \ 237d4afb5ceSopenharmony_ci sizeof(struct per_session_data__minimal), \ 238d4afb5ceSopenharmony_ci 128, \ 239d4afb5ceSopenharmony_ci 0, NULL, 0 \ 240d4afb5ceSopenharmony_ci } 241