static int callback_minimal_broker(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct per_vhost_data__minimal *vhd = (struct per_vhost_data__minimal *) lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); const struct msg *pmsg; void *retval; int n, m, r = 0; switch (reason) { /* --- protocol lifecycle callbacks --- */ case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__minimal)); vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); vhd->ring = lws_ring_create(sizeof(struct msg), 8, __minimal_destroy_message); if (!vhd->ring) return 1; pthread_mutex_init(&vhd->lock_ring, NULL); /* start the content-creating threads */ for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) if (pthread_create(&vhd->pthread_spam[n], NULL, thread_spam, vhd)) { lwsl_err("thread creation failed\n"); r = 1; goto init_fail; } if (connect_client(vhd)) lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, LWS_CALLBACK_USER, 1); break; case LWS_CALLBACK_PROTOCOL_DESTROY: init_fail: vhd->finished = 1; for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) if (vhd->pthread_spam[n]) pthread_join(vhd->pthread_spam[n], &retval); if (vhd->ring) lws_ring_destroy(vhd->ring); pthread_mutex_destroy(&vhd->lock_ring); return r; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); vhd->client_wsi = NULL; lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, LWS_CALLBACK_USER, 1); break; /* --- client callbacks --- */ case LWS_CALLBACK_CLIENT_ESTABLISHED: lwsl_user("%s: established\n", __func__); vhd->established = 1; break; case LWS_CALLBACK_CLIENT_WRITEABLE: pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ pmsg = lws_ring_get_element(vhd->ring, &vhd->tail); if (!pmsg) goto skip; /* notice we allowed for LWS_PRE in the payload already */ m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, pmsg->len, LWS_WRITE_TEXT); if (m < (int)pmsg->len) { pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock */ lwsl_err("ERROR %d writing to ws socket\n", m); return -1; } lws_ring_consume_single_tail(vhd->ring, &vhd->tail, 1); /* more to do for us? */ if (lws_ring_get_element(vhd->ring, &vhd->tail)) /* come back as soon as we can write more */ lws_callback_on_writable(wsi); skip: pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ break; case LWS_CALLBACK_CLIENT_CLOSED: vhd->client_wsi = NULL; vhd->established = 0; lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, LWS_CALLBACK_USER, 1); break; case LWS_CALLBACK_EVENT_WAIT_CANCELLED: /* * When the "spam" threads add a message to the ringbuffer, * they create this event in the lws service thread context * using lws_cancel_service(). * * We respond by scheduling a writable callback for the * connected client, if any. */ if (vhd && vhd->client_wsi && vhd->established) lws_callback_on_writable(vhd->client_wsi); break; /* rate-limited client connect retries */ case LWS_CALLBACK_USER: lwsl_notice("%s: LWS_CALLBACK_USER\n", __func__); if (connect_client(vhd)) lws_timed_callback_vh_protocol(vhd->vhost, vhd->protocol, LWS_CALLBACK_USER, 1); break; default: break; } return lws_callback_http_dummy(wsi, reason, user, in, len); }
static int callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct per_session_data__minimal *pss = (struct per_session_data__minimal *)user; struct per_vhost_data__minimal *vhd = (struct per_vhost_data__minimal *) lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); const struct lws_protocol_vhost_options *pvo; const struct msg *pmsg; void *retval; int n, m, r = 0; switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: /* create our per-vhost struct */ vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__minimal)); if (!vhd) return 1; pthread_mutex_init(&vhd->lock_ring, NULL); /* recover the pointer to the globals struct */ pvo = lws_pvo_search( (const struct lws_protocol_vhost_options *)in, "config"); if (!pvo || !pvo->value) { lwsl_err("%s: Can't find \"config\" pvo\n", __func__); return 1; } vhd->config = pvo->value; vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); vhd->ring = lws_ring_create(sizeof(struct msg), 8, __minimal_destroy_message); if (!vhd->ring) { lwsl_err("%s: failed to create ring\n", __func__); return 1; } /* start the content-creating threads */ for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) if (pthread_create(&vhd->pthread_spam[n], NULL, thread_spam, vhd)) { lwsl_err("thread creation failed\n"); r = 1; goto init_fail; } break; case LWS_CALLBACK_PROTOCOL_DESTROY: init_fail: vhd->finished = 1; for (n = 0; n < (int)LWS_ARRAY_SIZE(vhd->pthread_spam); n++) if (vhd->pthread_spam[n]) pthread_join(vhd->pthread_spam[n], &retval); if (vhd->ring) lws_ring_destroy(vhd->ring); pthread_mutex_destroy(&vhd->lock_ring); break; case LWS_CALLBACK_ESTABLISHED: /* add ourselves to the list of live pss held in the vhd */ lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); pss->tail = lws_ring_get_oldest_tail(vhd->ring); pss->wsi = wsi; break; case LWS_CALLBACK_CLOSED: /* remove our closing pss from the list of live pss */ lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, pss, vhd->pss_list); break; case LWS_CALLBACK_SERVER_WRITEABLE: pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ pmsg = lws_ring_get_element(vhd->ring, &pss->tail); if (!pmsg) { pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ break; } /* notice we allowed for LWS_PRE in the payload already */ m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, pmsg->len, LWS_WRITE_TEXT); if (m < (int)pmsg->len) { pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ lwsl_err("ERROR %d writing to ws socket\n", m); return -1; } lws_ring_consume_and_update_oldest_tail( vhd->ring, /* lws_ring object */ struct per_session_data__minimal, /* type of objects with tails */ &pss->tail, /* tail of guy doing the consuming */ 1, /* number of payload objects being consumed */ vhd->pss_list, /* head of list of objects with tails */ tail, /* member name of tail in objects with tails */ pss_list /* member name of next object in objects with tails */ ); /* more to do? */ if (lws_ring_get_element(vhd->ring, &pss->tail)) /* come back as soon as we can write more */ lws_callback_on_writable(pss->wsi); pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ break; case LWS_CALLBACK_RECEIVE: break; case LWS_CALLBACK_EVENT_WAIT_CANCELLED: /* * When the "spam" threads add a message to the ringbuffer, * they create this event in the lws service thread context * using lws_cancel_service(). * * We respond by scheduling a writable callback for all * connected clients. */ lws_start_foreach_llp(struct per_session_data__minimal **, ppss, vhd->pss_list) { lws_callback_on_writable((*ppss)->wsi); } lws_end_foreach_llp(ppss, pss_list); break; default: break; } return r; }
static int callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct per_session_data__minimal *pss = (struct per_session_data__minimal *)user; struct per_vhost_data__minimal *vhd = (struct per_vhost_data__minimal *) lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); const struct msg *pmsg; struct msg amsg; char buf[32]; int n, m; switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__minimal)); vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); vhd->ring = lws_ring_create(sizeof(struct msg), 8, __minimal_destroy_message); if (!vhd->ring) return 1; break; case LWS_CALLBACK_PROTOCOL_DESTROY: lws_ring_destroy(vhd->ring); break; case LWS_CALLBACK_ESTABLISHED: pss->tail = lws_ring_get_oldest_tail(vhd->ring); pss->wsi = wsi; if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI) > 0) pss->publishing = !strcmp(buf, "/publisher"); if (!pss->publishing) /* add subscribers to the list of live pss held in the vhd */ lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); break; case LWS_CALLBACK_CLOSED: /* remove our closing pss from the list of live pss */ lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, pss, vhd->pss_list); break; case LWS_CALLBACK_SERVER_WRITEABLE: if (pss->publishing) break; pmsg = lws_ring_get_element(vhd->ring, &pss->tail); if (!pmsg) break; /* notice we allowed for LWS_PRE in the payload already */ m = lws_write(wsi, ((unsigned char *)pmsg->payload) + LWS_PRE, pmsg->len, LWS_WRITE_TEXT); if (m < (int)pmsg->len) { lwsl_err("ERROR %d writing to ws socket\n", m); return -1; } lws_ring_consume_and_update_oldest_tail( vhd->ring, /* lws_ring object */ struct per_session_data__minimal, /* type of objects with tails */ &pss->tail, /* tail of guy doing the consuming */ 1, /* number of payload objects being consumed */ vhd->pss_list, /* head of list of objects with tails */ tail, /* member name of tail in objects with tails */ pss_list /* member name of next object in objects with tails */ ); /* more to do? */ if (lws_ring_get_element(vhd->ring, &pss->tail)) /* come back as soon as we can write more */ lws_callback_on_writable(pss->wsi); break; case LWS_CALLBACK_RECEIVE: if (!pss->publishing) break; /* * For test, our policy is ignore publishing when there are * no subscribers connected. */ if (!vhd->pss_list) break; n = (int)lws_ring_get_count_free_elements(vhd->ring); if (!n) { lwsl_user("dropping!\n"); break; } amsg.len = len; /* notice we over-allocate by LWS_PRE */ amsg.payload = malloc(LWS_PRE + len); if (!amsg.payload) { lwsl_user("OOM: dropping\n"); break; } memcpy((char *)amsg.payload + LWS_PRE, in, len); if (!lws_ring_insert(vhd->ring, &amsg, 1)) { __minimal_destroy_message(&amsg); lwsl_user("dropping 2!\n"); break; } /* * let every subscriber know we want to write something * on them as soon as they are ready */ lws_start_foreach_llp(struct per_session_data__minimal **, ppss, vhd->pss_list) { if (!(*ppss)->publishing) lws_callback_on_writable((*ppss)->wsi); } lws_end_foreach_llp(ppss, pss_list); break; default: break; } return 0; }