static void * thread_spam(void *d) { struct per_vhost_data__minimal *vhd = (struct per_vhost_data__minimal *)d; struct msg amsg; int len = 128, index = 1, n; do { /* don't generate output if client not connected */ if (!vhd->established) goto wait; pthread_mutex_lock(&vhd->lock_ring); /* --------- ring lock { */ /* only create if space in ringbuffer */ n = (int)lws_ring_get_count_free_elements(vhd->ring); if (!n) { lwsl_user("dropping!\n"); goto wait_unlock; } amsg.payload = malloc(LWS_PRE + len); if (!amsg.payload) { lwsl_user("OOM: dropping\n"); goto wait_unlock; } n = lws_snprintf((char *)amsg.payload + LWS_PRE, len, "tid: %p, msg: %d", (void *)pthread_self(), index++); amsg.len = n; n = lws_ring_insert(vhd->ring, &amsg, 1); if (n != 1) { __minimal_destroy_message(&amsg); lwsl_user("dropping!\n"); } else /* * This will cause a LWS_CALLBACK_EVENT_WAIT_CANCELLED * in the lws service thread context. */ lws_cancel_service(vhd->context); wait_unlock: pthread_mutex_unlock(&vhd->lock_ring); /* } ring lock ------- */ wait: usleep(100000); } while (!vhd->finished); lwsl_notice("thread_spam %p exiting\n", (void *)pthread_self()); pthread_exit(NULL); return NULL; }
/* Thread */ void *janus_websockets_thread(void *data) { struct lws_context *service = (struct lws_context *)data; if(service == NULL) { JANUS_LOG(LOG_ERR, "Invalid service\n"); return NULL; } JANUS_LOG(LOG_INFO, "WebSockets thread started\n"); while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) { /* libwebsockets is single thread, we cycle through events here */ lws_service(service, 50); } /* Get rid of the WebSockets server */ lws_cancel_service(service); /* Done */ JANUS_LOG(LOG_INFO, "WebSockets thread ended\n"); return NULL; }
static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { switch (reason) { /* because we are protocols[0] ... */ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); client_wsi = NULL; break; case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: status = lws_http_client_http_response(wsi); lwsl_user("Connected with server response: %d\n", status); break; /* chunks of chunked content, with header removed */ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); #if 0 /* enable to dump the html */ { const char *p = in; while (len--) if (*p < 0x7f) putchar(*p++); else putchar('.'); } #endif return 0; /* don't passthru */ /* uninterpreted http content */ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: { char buffer[1024 + LWS_PRE]; char *px = buffer + LWS_PRE; int lenx = sizeof(buffer) - LWS_PRE; if (lws_http_client_read(wsi, &px, &lenx) < 0) return -1; } return 0; /* don't passthru */ case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); client_wsi = NULL; bad = status != 200; lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ break; case LWS_CALLBACK_CLOSED_CLIENT_HTTP: client_wsi = NULL; bad = status != 200; lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ break; default: break; } return lws_callback_http_dummy(wsi, reason, user, in, len); }
void sighandler(int sig) { force_exit = 1; lws_cancel_service(context); }
int janus_websockets_send_message(void *transport, void *request_id, gboolean admin, json_t *message) { if(message == NULL) return -1; if(transport == NULL) { json_decref(message); return -1; } /* Make sure this is not related to a closed /freed WebSocket session */ janus_mutex_lock(&old_wss_mutex); janus_websockets_client *client = (janus_websockets_client *)transport; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI if(g_list_find(old_wss, client) != NULL || !client->wsi) { #else if(g_list_find(old_wss, client) != NULL || !client->context || !client->wsi) { #endif json_decref(message); message = NULL; transport = NULL; janus_mutex_unlock(&old_wss_mutex); return -1; } janus_mutex_lock(&client->mutex); /* Convert to string and enqueue */ char *payload = json_dumps(message, json_format); g_async_queue_push(client->messages, payload); #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_callback_on_writable(client->wsi); #else libwebsocket_callback_on_writable(client->context, client->wsi); #endif janus_mutex_unlock(&client->mutex); janus_mutex_unlock(&old_wss_mutex); json_decref(message); return 0; } void janus_websockets_session_created(void *transport, guint64 session_id) { /* We don't care */ } void janus_websockets_session_over(void *transport, guint64 session_id, gboolean timeout) { if(transport == NULL || !timeout) return; /* We only care if it's a timeout: if so, close the connection */ janus_websockets_client *client = (janus_websockets_client *)transport; /* Make sure this is not related to a closed WebSocket session */ janus_mutex_lock(&old_wss_mutex); #ifdef HAVE_LIBWEBSOCKETS_NEWAPI if(g_list_find(old_wss, client) == NULL && client->wsi){ #else if(g_list_find(old_wss, client) == NULL && client->context && client->wsi){ #endif janus_mutex_lock(&client->mutex); client->session_timeout = 1; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_callback_on_writable(client->wsi); #else libwebsocket_callback_on_writable(client->context, client->wsi); #endif janus_mutex_unlock(&client->mutex); } janus_mutex_unlock(&old_wss_mutex); } /* Thread */ void *janus_websockets_thread(void *data) { #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws_context *service = (struct lws_context *)data; #else struct libwebsocket_context *service = (struct libwebsocket_context *)data; #endif if(service == NULL) { JANUS_LOG(LOG_ERR, "Invalid service\n"); return NULL; } const char *type = NULL; if(service == wss) type = "WebSocket (Janus API)"; else if(service == swss) type = "Secure WebSocket (Janus API)"; else if(service == admin_wss) type = "WebSocket (Admin API)"; else if(service == admin_swss) type = "Secure WebSocket (Admin API)"; JANUS_LOG(LOG_INFO, "%s thread started\n", type); while(g_atomic_int_get(&initialized) && !g_atomic_int_get(&stopping)) { /* libwebsockets is single thread, we cycle through events here */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_service(service, 50); #else libwebsocket_service(service, 50); #endif } /* Get rid of the WebSockets server */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_cancel_service(service); #else libwebsocket_cancel_service(service); #endif /* Done */ JANUS_LOG(LOG_INFO, "%s thread ended\n", type); return NULL; } /* WebSockets */ static int janus_websockets_callback_http( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len) { /* This endpoint cannot be used for HTTP */ switch(reason) { case LWS_CALLBACK_HTTP: JANUS_LOG(LOG_VERB, "Rejecting incoming HTTP request on WebSockets endpoint\n"); #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_return_http_status(wsi, 403, NULL); #else libwebsockets_return_http_status(this, wsi, 403, NULL); #endif /* Close and free connection */ return -1; case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: if (!in) { JANUS_LOG(LOG_VERB, "Rejecting incoming HTTP request on WebSockets endpoint: no sub-protocol specified\n"); return -1; } break; default: break; } return 0; } static int janus_websockets_callback_https( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len) { /* We just forward the event to the HTTP handler */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI return janus_websockets_callback_http(wsi, reason, user, in, len); #else return janus_websockets_callback_http(this, wsi, reason, user, in, len); #endif } /* This callback handles Janus API requests */ static int janus_websockets_common_callback( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len, gboolean admin) { const char *log_prefix = admin ? "AdminWSS" : "WSS"; janus_websockets_client *ws_client = (janus_websockets_client *)user; switch(reason) { case LWS_CALLBACK_ESTABLISHED: { /* Is there any filtering we should apply? */ char name[256], ip[256]; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, 256, ip, 256); #else libwebsockets_get_peer_addresses(this, wsi, libwebsocket_get_socket_fd(wsi), name, 256, ip, 256); #endif JANUS_LOG(LOG_VERB, "[%s-%p] WebSocket connection opened from %s by %s\n", log_prefix, wsi, ip, name); if(!janus_websockets_is_allowed(ip, admin)) { JANUS_LOG(LOG_ERR, "[%s-%p] IP %s is unauthorized to connect to the WebSockets %s API interface\n", log_prefix, wsi, ip, admin ? "Admin" : "Janus"); /* Close the connection */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_callback_on_writable(wsi); #else libwebsocket_callback_on_writable(this, wsi); #endif return -1; } JANUS_LOG(LOG_VERB, "[%s-%p] WebSocket connection accepted\n", log_prefix, wsi); if(ws_client == NULL) { JANUS_LOG(LOG_ERR, "[%s-%p] Invalid WebSocket client instance...\n", log_prefix, wsi); return -1; } /* Clean the old sessions list, in case this pointer was used before */ janus_mutex_lock(&old_wss_mutex); if(g_list_find(old_wss, ws_client) != NULL) old_wss = g_list_remove(old_wss, ws_client); janus_mutex_unlock(&old_wss_mutex); /* Prepare the session */ #ifndef HAVE_LIBWEBSOCKETS_NEWAPI ws_client->context = this; #endif ws_client->wsi = wsi; ws_client->messages = g_async_queue_new(); ws_client->buffer = NULL; ws_client->buflen = 0; ws_client->bufpending = 0; ws_client->bufoffset = 0; ws_client->session_timeout = 0; ws_client->destroy = 0; janus_mutex_init(&ws_client->mutex); /* Let us know when the WebSocket channel becomes writeable */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_callback_on_writable(wsi); #else libwebsocket_callback_on_writable(this, wsi); #endif JANUS_LOG(LOG_VERB, "[%s-%p] -- Ready to be used!\n", log_prefix, wsi); /* Notify handlers about this new transport */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("connected")); json_object_set_new(info, "admin_api", admin ? json_true() : json_false()); json_object_set_new(info, "ip", json_string(ip)); gateway->notify_event(&janus_websockets_transport, ws_client, info); } return 0; } case LWS_CALLBACK_RECEIVE: { JANUS_LOG(LOG_HUGE, "[%s-%p] Got %zu bytes:\n", log_prefix, wsi, len); #ifdef HAVE_LIBWEBSOCKETS_NEWAPI if(ws_client == NULL || ws_client->wsi == NULL) { #else if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) { #endif JANUS_LOG(LOG_ERR, "[%s-%p] Invalid WebSocket client instance...\n", log_prefix, wsi); return -1; } /* Is this a new message, or part of a fragmented one? */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI const size_t remaining = lws_remaining_packet_payload(wsi); #else const size_t remaining = libwebsockets_remaining_packet_payload(wsi); #endif if(ws_client->incoming == NULL) { JANUS_LOG(LOG_HUGE, "[%s-%p] First fragment: %zu bytes, %zu remaining\n", log_prefix, wsi, len, remaining); ws_client->incoming = g_malloc0(len+1); memcpy(ws_client->incoming, in, len); ws_client->incoming[len] = '\0'; JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming); } else { size_t offset = strlen(ws_client->incoming); JANUS_LOG(LOG_HUGE, "[%s-%p] Appending fragment: offset %zu, %zu bytes, %zu remaining\n", log_prefix, wsi, offset, len, remaining); ws_client->incoming = g_realloc(ws_client->incoming, offset+len+1); memcpy(ws_client->incoming+offset, in, len); ws_client->incoming[offset+len] = '\0'; JANUS_LOG(LOG_HUGE, "%s\n", ws_client->incoming+offset); } #ifdef HAVE_LIBWEBSOCKETS_NEWAPI if(remaining > 0 || !lws_is_final_fragment(wsi)) { #else if(remaining > 0 || !libwebsocket_is_final_fragment(wsi)) { #endif /* Still waiting for some more fragments */ JANUS_LOG(LOG_HUGE, "[%s-%p] Waiting for more fragments\n", log_prefix, wsi); return 0; } JANUS_LOG(LOG_HUGE, "[%s-%p] Done, parsing message: %zu bytes\n", log_prefix, wsi, strlen(ws_client->incoming)); /* If we got here, the message is complete: parse the JSON payload */ json_error_t error; json_t *root = json_loads(ws_client->incoming, 0, &error); g_free(ws_client->incoming); ws_client->incoming = NULL; /* Notify the core, passing both the object and, since it may be needed, the error */ gateway->incoming_request(&janus_websockets_transport, ws_client, NULL, admin, root, &error); return 0; } case LWS_CALLBACK_SERVER_WRITEABLE: { #ifdef HAVE_LIBWEBSOCKETS_NEWAPI if(ws_client == NULL || ws_client->wsi == NULL) { #else if(ws_client == NULL || ws_client->context == NULL || ws_client->wsi == NULL) { #endif JANUS_LOG(LOG_ERR, "[%s-%p] Invalid WebSocket client instance...\n", log_prefix, wsi); return -1; } if(!ws_client->destroy && !g_atomic_int_get(&stopping)) { janus_mutex_lock(&ws_client->mutex); /* Check if we have a pending/partial write to complete first */ if(ws_client->buffer && ws_client->bufpending > 0 && ws_client->bufoffset > 0 && !ws_client->destroy && !g_atomic_int_get(&stopping)) { JANUS_LOG(LOG_HUGE, "[%s-%p] Completing pending WebSocket write (still need to write last %d bytes)...\n", log_prefix, wsi, ws_client->bufpending); #ifdef HAVE_LIBWEBSOCKETS_NEWAPI int sent = lws_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT); #else int sent = libwebsocket_write(wsi, ws_client->buffer + ws_client->bufoffset, ws_client->bufpending, LWS_WRITE_TEXT); #endif JANUS_LOG(LOG_HUGE, "[%s-%p] -- Sent %d/%d bytes\n", log_prefix, wsi, sent, ws_client->bufpending); if(sent > -1 && sent < ws_client->bufpending) { /* We still couldn't send everything that was left, we'll try and complete this in the next round */ ws_client->bufpending -= sent; ws_client->bufoffset += sent; } else { /* Clear the pending/partial write queue */ ws_client->bufpending = 0; ws_client->bufoffset = 0; } /* Done for this round, check the next response/notification later */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_callback_on_writable(wsi); #else libwebsocket_callback_on_writable(this, wsi); #endif janus_mutex_unlock(&ws_client->mutex); return 0; } /* Shoot all the pending messages */ char *response = g_async_queue_try_pop(ws_client->messages); if(response && !ws_client->destroy && !g_atomic_int_get(&stopping)) { /* Gotcha! */ int buflen = LWS_SEND_BUFFER_PRE_PADDING + strlen(response) + LWS_SEND_BUFFER_POST_PADDING; if(ws_client->buffer == NULL) { /* Let's allocate a shared buffer */ JANUS_LOG(LOG_HUGE, "[%s-%p] Allocating %d bytes (response is %zu bytes)\n", log_prefix, wsi, buflen, strlen(response)); ws_client->buflen = buflen; ws_client->buffer = g_malloc0(buflen); } else if(buflen > ws_client->buflen) { /* We need a larger shared buffer */ JANUS_LOG(LOG_HUGE, "[%s-%p] Re-allocating to %d bytes (was %d, response is %zu bytes)\n", log_prefix, wsi, buflen, ws_client->buflen, strlen(response)); ws_client->buflen = buflen; ws_client->buffer = g_realloc(ws_client->buffer, buflen); } memcpy(ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, response, strlen(response)); JANUS_LOG(LOG_HUGE, "[%s-%p] Sending WebSocket message (%zu bytes)...\n", log_prefix, wsi, strlen(response)); #ifdef HAVE_LIBWEBSOCKETS_NEWAPI int sent = lws_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT); #else int sent = libwebsocket_write(wsi, ws_client->buffer + LWS_SEND_BUFFER_PRE_PADDING, strlen(response), LWS_WRITE_TEXT); #endif JANUS_LOG(LOG_HUGE, "[%s-%p] -- Sent %d/%zu bytes\n", log_prefix, wsi, sent, strlen(response)); if(sent > -1 && sent < (int)strlen(response)) { /* We couldn't send everything in a single write, we'll complete this in the next round */ ws_client->bufpending = strlen(response) - sent; ws_client->bufoffset = LWS_SEND_BUFFER_PRE_PADDING + sent; JANUS_LOG(LOG_HUGE, "[%s-%p] -- Couldn't write all bytes (%d missing), setting offset %d\n", log_prefix, wsi, ws_client->bufpending, ws_client->bufoffset); } /* We can get rid of the message */ free(response); /* Done for this round, check the next response/notification later */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI lws_callback_on_writable(wsi); #else libwebsocket_callback_on_writable(this, wsi); #endif janus_mutex_unlock(&ws_client->mutex); return 0; } janus_mutex_unlock(&ws_client->mutex); } return 0; } case LWS_CALLBACK_CLOSED: { JANUS_LOG(LOG_VERB, "[%s-%p] WS connection down, closing\n", log_prefix, wsi); janus_websockets_destroy_client(ws_client, wsi, log_prefix); JANUS_LOG(LOG_VERB, "[%s-%p] -- closed\n", log_prefix, wsi); return 0; } case LWS_CALLBACK_WSI_DESTROY: { JANUS_LOG(LOG_VERB, "[%s-%p] WS connection down, destroying\n", log_prefix, wsi); janus_websockets_destroy_client(ws_client, wsi, log_prefix); JANUS_LOG(LOG_VERB, "[%s-%p] -- destroyed\n", log_prefix, wsi); return 0; } default: if(wsi != NULL) { JANUS_LOG(LOG_HUGE, "[%s-%p] %d (%s)\n", log_prefix, wsi, reason, janus_websockets_reason_string(reason)); } else { JANUS_LOG(LOG_HUGE, "[%s] %d (%s)\n", log_prefix, reason, janus_websockets_reason_string(reason)); } break; } return 0; } /* This callback handles Janus API requests */ static int janus_websockets_callback( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len) { #ifdef HAVE_LIBWEBSOCKETS_NEWAPI return janus_websockets_common_callback(wsi, reason, user, in, len, FALSE); #else return janus_websockets_common_callback(this, wsi, reason, user, in, len, FALSE); #endif } static int janus_websockets_callback_secure( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len) { /* We just forward the event to the Janus API handler */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI return janus_websockets_callback(wsi, reason, user, in, len); #else return janus_websockets_callback(this, wsi, reason, user, in, len); #endif } /* This callback handles Admin API requests */ static int janus_websockets_admin_callback( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len) { #ifdef HAVE_LIBWEBSOCKETS_NEWAPI return janus_websockets_common_callback(wsi, reason, user, in, len, TRUE); #else return janus_websockets_common_callback(this, wsi, reason, user, in, len, TRUE); #endif } static int janus_websockets_admin_callback_secure( #ifdef HAVE_LIBWEBSOCKETS_NEWAPI struct lws *wsi, enum lws_callback_reasons reason, #else struct libwebsocket_context *this, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, #endif void *user, void *in, size_t len) { /* We just forward the event to the Admin API handler */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI return janus_websockets_admin_callback(wsi, reason, user, in, len); #else return janus_websockets_admin_callback(this, wsi, reason, user, in, len); #endif }
int lws_change_pollfd(struct lws *wsi, int _and, int _or) { struct lws_context *context; int tid; int sampled_tid; struct lws_pollfd *pfd; struct lws_pollargs pa; int pa_events = 1; if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0) return 1; context = lws_get_context(wsi); if (!context) return 1; pfd = &context->fds[wsi->position_in_fds_table]; pa.fd = wsi->sock; if (context->protocols[0].callback(wsi, LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0)) return -1; pa.prev_events = pfd->events; pa.events = pfd->events = (pfd->events & ~_and) | _or; if (context->protocols[0].callback(wsi, LWS_CALLBACK_CHANGE_MODE_POLL_FD, wsi->user_space, (void *) &pa, 0)) return -1; /* * if we changed something in this pollfd... * ... and we're running in a different thread context * than the service thread... * ... and the service thread is waiting ... * then cancel it to force a restart with our changed events */ #if LWS_POSIX pa_events = pa.prev_events != pa.events; #endif if (pa_events) { if (lws_plat_change_pollfd(context, wsi, pfd)) { lwsl_info("%s failed\n", __func__); return 1; } sampled_tid = context->service_tid; if (sampled_tid) { tid = context->protocols[0].callback(wsi, LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); if (tid == -1) return -1; if (tid != sampled_tid) lws_cancel_service(context); } } if (context->protocols[0].callback(wsi, LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0)) return -1; return 0; }
static int discord_ws_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct im_connection *ic = NULL; discord_data *dd = NULL; if (wsi == NULL) { return 0; } struct lws_context *wsctx = lws_get_context(wsi); if (wsctx != NULL) { ic = lws_context_user(wsctx); dd = ic->proto_data; } switch(reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: dd->state = WS_CONNECTED; lws_callback_on_writable(wsi); break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: imcb_error(ic, "Websocket connection error"); if (in != NULL) { imcb_error(ic, in); } b_event_remove(dd->keepalive_loop_id); dd->keepalive_loop_id = 0; dd->state = WS_CLOSING; break; case LWS_CALLBACK_CLIENT_WRITEABLE: if (dd->state == WS_CONNECTED) { GString *buf = g_string_new(""); g_string_printf(buf, "{\"d\":{\"v\":3,\"token\":\"%s\",\"properties\":{\"$referring_domain\":\"\",\"$browser\":\"bitlbee-discord\",\"$device\":\"bitlbee\",\"$referrer\":\"\",\"$os\":\"linux\"}},\"op\":2}", dd->token); discord_ws_send_payload(wsi, buf->str, buf->len); g_string_free(buf, TRUE); } else if (dd->state == WS_READY) { GString *buf = g_string_new(""); g_string_printf(buf, "{\"op\":1,\"d\":%tu}", time(NULL)); discord_ws_send_payload(dd->lws, buf->str, buf->len); g_string_free(buf, TRUE); } else { g_print("%s: Unhandled writable callback\n", __func__); } break; case LWS_CALLBACK_CLIENT_RECEIVE: { size_t rpload = lws_remaining_packet_payload(wsi); if (dd->ws_buf == NULL) { dd->ws_buf = g_string_new(""); } dd->ws_buf = g_string_append(dd->ws_buf, in); if (rpload == 0) { discord_parse_message(ic); g_string_free(dd->ws_buf, TRUE); dd->ws_buf = NULL; } break; } case LWS_CALLBACK_CLOSED: b_event_remove(dd->keepalive_loop_id); dd->keepalive_loop_id = 0; dd->state = WS_CLOSING; lws_cancel_service(dd->lwsctx); break; case LWS_CALLBACK_ADD_POLL_FD: { struct lws_pollargs *pargs = in; dd->main_loop_id = b_input_add(pargs->fd, B_EV_IO_READ, discord_ws_service_loop, ic); break; } case LWS_CALLBACK_DEL_POLL_FD: b_event_remove(dd->main_loop_id); break; case LWS_CALLBACK_CHANGE_MODE_POLL_FD: { struct lws_pollargs *pargs = in; int flags = 0; b_event_remove(dd->main_loop_id); if (pargs->events & POLLIN) { flags |= B_EV_IO_READ; } if (pargs->events & POLLOUT) { flags |= B_EV_IO_WRITE; } dd->main_loop_id = b_input_add(pargs->fd, flags, discord_ws_service_loop, ic); break; } case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED: case LWS_CALLBACK_GET_THREAD_ID: case LWS_CALLBACK_LOCK_POLL: case LWS_CALLBACK_UNLOCK_POLL: case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: case LWS_CALLBACK_PROTOCOL_INIT: case LWS_CALLBACK_PROTOCOL_DESTROY: case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: case LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH: case LWS_CALLBACK_WSI_CREATE: case LWS_CALLBACK_WSI_DESTROY: // Ignoring these, this block should be removed when defult is set to // stay silent. break; default: g_print("%s: unknown rsn=%d\n", __func__, reason); break; } return 0; }
LWS_VISIBLE LWS_EXTERN void libwebsocket_cancel_service(struct lws_context *context) { lws_cancel_service(context); }
void sighandler(int sig __attribute__((unused))) { force_exit = 1; lws_cancel_service(context); }
static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { char val[32]; int n; switch (reason) { /* because we are protocols[0] ... */ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", in ? (char *)in : "(null)"); client_wsi = NULL; break; case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: { unsigned char **p = (unsigned char **)in, *end = (*p) + len; /* * How to send a custom header in the request to the server */ if (lws_add_http_header_by_name(wsi, (const unsigned char *)"dnt", (const unsigned char *)"1", 1, p, end)) return -1; break; } case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: status = lws_http_client_http_response(wsi); lwsl_user("Connected with server response: %d\n", status); /* * How to query custom headers (http 1.x only at the momemnt) * * warmcat.com sends a custom header "test-custom-header" for * testing, it has the fixed value "hello". */ n = lws_hdr_custom_length(wsi, "test-custom-header:", 19); if (n < 0) lwsl_notice("%s: Can't find test-custom-header\n", __func__); else { if (lws_hdr_custom_copy(wsi, val, sizeof(val), "test-custom-header:", 19) < 0) lwsl_notice("%s: custom header too long\n", __func__); else lwsl_notice("%s: custom header: '%s'\n", __func__, val); } break; /* chunks of chunked content, with header removed */ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); #if 0 /* enable to dump the html */ { const char *p = in; while (len--) if (*p < 0x7f) putchar(*p++); else putchar('.'); } #endif return 0; /* don't passthru */ /* uninterpreted http content */ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: { char buffer[1024 + LWS_PRE]; char *px = buffer + LWS_PRE; int lenx = sizeof(buffer) - LWS_PRE; if (lws_http_client_read(wsi, &px, &lenx) < 0) return -1; } return 0; /* don't passthru */ case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); client_wsi = NULL; bad = status != 200; lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ break; case LWS_CALLBACK_CLOSED_CLIENT_HTTP: client_wsi = NULL; bad = status != 200; lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ break; default: break; } return lws_callback_http_dummy(wsi, reason, user, in, len); }