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 }
static char *http__canonical_filename( #ifndef LWS_LIBRARY_VERSION_NUMBER struct libwebsocket_context *context, #endif struct libwebsocket *wsi, const char *in, const char *http_dir) { size_t inlen, slen; char *filename, *filename_canonical; inlen = strlen(in); if(in[inlen-1] == '/'){ slen = strlen(http_dir) + inlen + strlen("/index.html") + 2; }else{ slen = strlen(http_dir) + inlen + 2; } filename = mosquitto__malloc(slen); if(!filename){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); return NULL; } if(((char *)in)[inlen-1] == '/'){ snprintf(filename, slen, "%s%sindex.html", http_dir, (char *)in); }else{ snprintf(filename, slen, "%s%s", http_dir, (char *)in); } /* Get canonical path and check it is within our http_dir */ #ifdef WIN32 filename_canonical = _fullpath(NULL, filename, 0); mosquitto__free(filename); if(!filename_canonical){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); return NULL; } #else filename_canonical = realpath(filename, NULL); mosquitto__free(filename); if(!filename_canonical){ if(errno == EACCES){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); }else if(errno == EINVAL || errno == EIO || errno == ELOOP){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); }else if(errno == ENAMETOOLONG){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_REQ_URI_TOO_LONG, NULL); }else if(errno == ENOENT || errno == ENOTDIR){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_NOT_FOUND, NULL); } return NULL; } #endif if(strncmp(http_dir, filename_canonical, strlen(http_dir))){ /* Requested file isn't within http_dir, deny access. */ free(filename_canonical); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); return NULL; } return filename_canonical; }
static int callback_http(struct libwebsocket_context *context, #endif struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { struct libws_http_data *u = (struct libws_http_data *)user; struct libws_mqtt_hack *hack; char *http_dir; size_t buflen; size_t wlen; char *filename_canonical; unsigned char buf[4096]; struct stat filestat; struct mosquitto_db *db = &int_db; struct mosquitto *mosq; struct lws_pollargs *pollargs = (struct lws_pollargs *)in; /* FIXME - ssl cert verification is done here. */ switch (reason) { case LWS_CALLBACK_HTTP: if(!u){ return -1; } #if defined(LWS_LIBRARY_VERSION_NUMBER) hack = (struct libws_mqtt_hack *)lws_context_user(lws_get_context(wsi)); #else hack = (struct libws_mqtt_hack *)libwebsocket_context_user(context); #endif if(!hack){ return -1; } http_dir = hack->http_dir; if(!http_dir){ /* http disabled */ return -1; } /* Forbid POST */ if(lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL); return -1; } #if defined(LWS_LIBRARY_VERSION_NUMBER) filename_canonical = http__canonical_filename(wsi, (char *)in, http_dir); #else filename_canonical = http__canonical_filename(context, wsi, (char *)in, http_dir); #endif if(!filename_canonical) return -1; u->fptr = fopen(filename_canonical, "rb"); if(!u->fptr){ free(filename_canonical); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_NOT_FOUND, NULL); return -1; } if(fstat(fileno(u->fptr), &filestat) < 0){ free(filename_canonical); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); fclose(u->fptr); u->fptr = NULL; return -1; } if((filestat.st_mode & S_IFDIR) == S_IFDIR){ fclose(u->fptr); u->fptr = NULL; free(filename_canonical); /* FIXME - use header functions from lws 2.x */ buflen = snprintf((char *)buf, 4096, "HTTP/1.0 302 OK\r\n" "Location: %s/\r\n\r\n", (char *)in); return libwebsocket_write(wsi, buf, buflen, LWS_WRITE_HTTP); } if((filestat.st_mode & S_IFREG) != S_IFREG){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); fclose(u->fptr); u->fptr = NULL; free(filename_canonical); return -1; } log__printf(NULL, MOSQ_LOG_DEBUG, "http serving file \"%s\".", filename_canonical); free(filename_canonical); /* FIXME - use header functions from lws 2.x */ buflen = snprintf((char *)buf, 4096, "HTTP/1.0 200 OK\r\n" "Server: mosquitto\r\n" "Content-Length: %u\r\n\r\n", (unsigned int)filestat.st_size); if(libwebsocket_write(wsi, buf, buflen, LWS_WRITE_HTTP) < 0){ fclose(u->fptr); u->fptr = NULL; return -1; } libwebsocket_callback_on_writable(context, wsi); break; case LWS_CALLBACK_HTTP_BODY: /* For extra POST data? */ return -1; case LWS_CALLBACK_HTTP_BODY_COMPLETION: /* For end of extra POST data? */ return -1; case LWS_CALLBACK_FILTER_HTTP_CONNECTION: /* Access control here */ return 0; case LWS_CALLBACK_HTTP_WRITEABLE: /* Send our data here */ if(u && u->fptr){ do{ buflen = fread(buf, 1, sizeof(buf), u->fptr); if(buflen < 1){ fclose(u->fptr); u->fptr = NULL; return -1; } wlen = libwebsocket_write(wsi, buf, buflen, LWS_WRITE_HTTP); if(wlen < buflen){ if(fseek(u->fptr, buflen-wlen, SEEK_CUR) < 0){ fclose(u->fptr); u->fptr = NULL; return -1; } }else{ if(buflen < sizeof(buf)){ fclose(u->fptr); u->fptr = NULL; } } }while(u->fptr && !lws_send_pipe_choked(wsi)); libwebsocket_callback_on_writable(context, wsi); }else{ return -1; } break; case LWS_CALLBACK_CLOSED: case LWS_CALLBACK_CLOSED_HTTP: case LWS_CALLBACK_HTTP_FILE_COMPLETION: if(u && u->fptr){ fclose(u->fptr); u->fptr = NULL; } break; case LWS_CALLBACK_ADD_POLL_FD: case LWS_CALLBACK_DEL_POLL_FD: case LWS_CALLBACK_CHANGE_MODE_POLL_FD: HASH_FIND(hh_sock, db->contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); if(mosq && (pollargs->events & POLLOUT)){ mosq->ws_want_write = true; } break; #ifdef WITH_TLS case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: if(!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)){ return 1; } break; #endif default: return 0; } return 0; }
static int callback_http(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { #if 0 char client_name[128]; char client_ip[128]; #endif char buf[256]; char leaf_path[1024]; char b64[64]; struct timeval tv; int n, m; unsigned char *p; char *other_headers; static unsigned char buffer[4096]; struct stat stat_buf; struct per_session_data__http *pss = (struct per_session_data__http *)user; const char *mimetype; #ifdef EXTERNAL_POLL struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in; #endif unsigned char *end; switch (reason) { case LWS_CALLBACK_HTTP: dump_handshake_info(wsi); if (len < 1) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL); goto try_to_reuse; } /* this example server has no concept of directories */ if (strchr((const char *)in + 1, '/')) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); goto try_to_reuse; } /* if a legal POST URL, let it continue and accept data */ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) return 0; /* check for the "send a big file by hand" example case */ if (!strcmp((const char *)in, "/leaf.jpg")) { if (strlen(resource_path) > sizeof(leaf_path) - 10) return -1; sprintf(leaf_path, "%s/leaf.jpg", resource_path); /* well, let's demonstrate how to send the hard way */ p = buffer + LWS_SEND_BUFFER_PRE_PADDING; end = p + sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING; #ifdef WIN32 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY); #else pss->fd = open(leaf_path, O_RDONLY); #endif if (pss->fd < 0) return -1; fstat(pss->fd, &stat_buf); /* * we will send a big jpeg file, but it could be * anything. Set the Content-Type: appropriately * so the browser knows what to do with it. * * Notice we use the APIs to build the header, which * will do the right thing for HTTP 1/1.1 and HTTP2 * depending on what connection it happens to be working * on */ if (lws_add_http_header_status(context, wsi, 200, &p, end)) return 1; if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end)) return 1; if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"image/jpeg", 10, &p, end)) return 1; if (lws_add_http_header_content_length(context, wsi,stat_buf.st_size, &p, end)) return 1; if (lws_finalize_http_header(context, wsi, &p, end)) return 1; /* * send the http headers... * this won't block since it's the first payload sent * on the connection since it was established * (too small for partial) * * Notice they are sent using LWS_WRITE_HTTP_HEADERS * which also means you can't send body too in one step, * this is mandated by changes in HTTP2 */ n = libwebsocket_write(wsi, buffer + LWS_SEND_BUFFER_PRE_PADDING, p - (buffer + LWS_SEND_BUFFER_PRE_PADDING), LWS_WRITE_HTTP_HEADERS); if (n < 0) { close(pss->fd); return -1; } /* * book us a LWS_CALLBACK_HTTP_WRITEABLE callback */ libwebsocket_callback_on_writable(context, wsi); break; } /* if not, send a file the easy way */ strcpy(buf, resource_path); if (strcmp(in, "/")) { if (*((const char *)in) != '/') strcat(buf, "/"); strncat(buf, in, sizeof(buf) - strlen(resource_path)); } else /* default file to serve */ strcat(buf, "/test.html"); buf[sizeof(buf) - 1] = '\0'; /* refuse to serve files we don't understand */ mimetype = get_mimetype(buf); if (!mimetype) { lwsl_err("Unknown mimetype for %s\n", buf); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL); return -1; } /* demostrates how to set a cookie on / */ other_headers = NULL; n = 0; if (!strcmp((const char *)in, "/") && !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { /* this isn't very unguessable but it'll do for us */ gettimeofday(&tv, NULL); n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec); p = (unsigned char *)leaf_path; if (lws_add_http_header_by_name(context, wsi, (unsigned char *)"set-cookie:", (unsigned char *)b64, n, &p, (unsigned char *)leaf_path + sizeof(leaf_path))) return 1; n = (char *)p - leaf_path; other_headers = leaf_path; } n = libwebsockets_serve_http_file(context, wsi, buf, mimetype, other_headers, n); if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) return -1; /* error or can't reuse connection: close the socket */ /* * notice that the sending of the file completes asynchronously, * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when * it's done */ break; case LWS_CALLBACK_HTTP_BODY: strncpy(buf, in, 20); buf[20] = '\0'; if (len < 20) buf[len] = '\0'; lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", (const char *)buf, (int)len); break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); /* the whole of the sent body arrived, close or reuse the connection */ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_OK, NULL); goto try_to_reuse; case LWS_CALLBACK_HTTP_FILE_COMPLETION: // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); /* kill the connection after we sent one file */ goto try_to_reuse; case LWS_CALLBACK_HTTP_WRITEABLE: /* * we can send more of whatever it is we were sending */ lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n"); do { lwsl_info("a\n"); n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING, sizeof (buffer) - LWS_SEND_BUFFER_PRE_PADDING); /* problem reading, close conn */ if (n < 0) goto bail; /* sent it all, close conn */ if (n == 0) goto flush_bail; lwsl_info("b\n"); /* * To support HTTP2, must take care about preamble space * and identify when we send the last frame */ m = libwebsocket_write(wsi, buffer + LWS_SEND_BUFFER_PRE_PADDING, n, LWS_WRITE_HTTP); if (m < 0) /* write failed, close conn */ goto bail; lwsl_info("c\n"); /* * http2 won't do this */ if (m != n) /* partial write, adjust */ lseek(pss->fd, m - n, SEEK_CUR); lwsl_info("d\n"); if (m) /* while still active, extend timeout */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5); /* if he has indigestion, let him clear it before eating more */ if (lws_partial_buffered(wsi)) break; } while (!lws_send_pipe_choked(wsi)); lwsl_info("e\n"); libwebsocket_callback_on_writable(context, wsi); lwsl_info("f\n"); break; flush_bail: /* true if still partial pending */ if (lws_partial_buffered(wsi)) { libwebsocket_callback_on_writable(context, wsi); break; } close(pss->fd); goto try_to_reuse; bail: close(pss->fd); return -1; /* * callback for confirming to continue with client IP appear in * protocol 0 callback since no websocket protocol has been agreed * yet. You can just ignore this if you won't filter on client IP * since the default uhandled callback return is 0 meaning let the * connection continue. */ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: #if 0 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name, sizeof(client_name), client_ip, sizeof(client_ip)); fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip); #endif /* if we returned non-zero from here, we kill the connection */ break; #ifdef EXTERNAL_POLL /* * callbacks for managing the external poll() array appear in * protocol 0 callback */ case LWS_CALLBACK_LOCK_POLL: /* * lock mutex to protect pollfd state * called before any other POLL related callback */ break; case LWS_CALLBACK_UNLOCK_POLL: /* * unlock mutex to protect pollfd state when * called after any other POLL related callback */ break; case LWS_CALLBACK_ADD_POLL_FD: if (count_pollfds >= max_poll_elements) { lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n"); return 1; } fd_lookup[pa->fd] = count_pollfds; pollfds[count_pollfds].fd = pa->fd; pollfds[count_pollfds].events = pa->events; pollfds[count_pollfds++].revents = 0; break; case LWS_CALLBACK_DEL_POLL_FD: if (!--count_pollfds) break; m = fd_lookup[pa->fd]; /* have the last guy take up the vacant slot */ pollfds[m] = pollfds[count_pollfds]; fd_lookup[pollfds[count_pollfds].fd] = m; break; case LWS_CALLBACK_CHANGE_MODE_POLL_FD: pollfds[fd_lookup[pa->fd]].events = pa->events; break; #endif case LWS_CALLBACK_GET_THREAD_ID: /* * if you will call "libwebsocket_callback_on_writable" * from a different thread, return the caller thread ID * here so lws can use this information to work out if it * should signal the poll() loop to exit and restart early */ /* return pthread_getthreadid_np(); */ break; default: break; } return 0; try_to_reuse: if (lws_http_transaction_completed(wsi)) return -1; return 0; }
static int callback_http(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { char buf[256]; char leaf_path[1024]; char b64[64]; struct timeval tv; int n, m; unsigned char *p; static unsigned char buffer[4096]; struct stat stat_buf; struct per_session_data__http *pss = (struct per_session_data__http *)user; const char *mimetype; switch (reason) { case LWS_CALLBACK_HTTP: if (len < 1) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL); return -1; } /* this server has no concept of directories */ if (strchr((const char *)in + 1, '/')) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); return -1; } /* if a legal POST URL, let it continue and accept data */ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) return 0; /* if not, send a file the easy way */ strcpy(buf, resource_path); if (strcmp(in, "/")) { if (*((const char *)in) != '/') strcat(buf, "/"); strncat(buf, in, sizeof(buf) - strlen(resource_path)); } else /* default file to serve */ strcat(buf, HTML_FILE); buf[sizeof(buf) - 1] = '\0'; /* refuse to serve files we don't understand */ mimetype = get_mimetype(buf); if (!mimetype) { lwsl_err("Unknown mimetype for %s\n", buf); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL); return -1; } if (libwebsockets_serve_http_file(context, wsi, buf, mimetype, NULL)) return -1; /* through completion or error, close the socket */ break; case LWS_CALLBACK_HTTP_BODY: strncpy(buf, in, 20); buf[20] = '\0'; if (len < 20) buf[len] = '\0'; lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", (const char *)buf, (int)len); break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); /* the whole of the sent body arried, close the connection */ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_OK, NULL); return -1; case LWS_CALLBACK_HTTP_FILE_COMPLETION: /* kill the connection after we sent one file */ return -1; case LWS_CALLBACK_HTTP_WRITEABLE: do { n = read(pss->fd, buffer, 4096); if (n <= 0) { close(pss->fd); return -1; } m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP); if (m < 0) { close(pss->fd); return -1; } if (m != n) /* partial write, adjust */ lseek(pss->fd, m - n, SEEK_CUR); } while (!lws_send_pipe_choked(wsi)); libwebsocket_callback_on_writable(context, wsi); break; default: break; } return 0; }
static int callback_nyx_websockets(struct libwebsocket_context *context,struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason, void *user,void *in,size_t len) { struct per_session_data_nyx *pss =(struct per_session_data_nyx*)user; int n; node *wsd_state = (node*)libwebsocket_context_user(context); //node *daemon = (node*)libwebsocket_context_user(context); //node *wsd_state = node_GetNode(get_value(daemon)); node *found_prot = NULL; node *state = NULL; node *block = NULL; //node *daemon = NULL; node *daemon_obj = NULL; node *session_uid = NULL; long lsession_uid = 0; node *sessions_num = NULL; node *sessions = NULL; long lsessions_num = 0; if(wsd_state) { state = node_GetItem(wsd_state,0); block = node_GetItem(wsd_state,1); //daemon = node_GetItem(wsd_state,2); node *protocols = node_GetItem(wsd_state,3); session_uid = node_GetItem(wsd_state,4); node *session_uid_value = node_GetItemByKey(session_uid,"value"); lsession_uid = node_GetSint32(session_uid_value); sessions_num = node_GetItem(wsd_state,5); sessions = node_GetItem(wsd_state,6); node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); lsessions_num = node_GetSint32(sessions_num_value); daemon_obj = node_GetItem(wsd_state,9); if(wsi) { node *protocols_items = node_GetItemByKey(protocols,"items"); const struct libwebsocket_protocols *prot = libwebsockets_get_protocol(wsi); if(prot && prot->name) { node_ItemIterationReset(protocols_items); while(node_ItemIterationUnfinished(protocols_items)) { node *proto = node_ItemIterate(protocols_items); if(!strcmp(get_obj_name(proto),prot->name)) { found_prot = proto; } } } } } switch(reason) { //case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED: case LWS_CALLBACK_CLIENT_ESTABLISHED: printf("new session created:%d, num:%d\n",lsession_uid,lsessions_num); pss->session = NULL; break; case LWS_CALLBACK_HTTP: if(len < 1) { libwebsockets_return_http_status(context,wsi,HTTP_STATUS_BAD_REQUEST,NULL); return(-1); } if(lws_hdr_total_length(wsi,WSI_TOKEN_POST_URI)) return(0); if(found_prot) { //printf("found prot in http callback : uid:%d,num:%d (sess:%x)\n",lsession_uid+1,lsessions_num,pss->session); if(!pss->session) { lsession_uid++; node *session_uid_value = node_GetItemByKey(session_uid,"value"); node_SetSint32(session_uid_value,lsession_uid); lsessions_num++; node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); node_SetSint32(sessions_num_value,lsessions_num); pss->session = create_session(state,sessions,lsession_uid,get_obj_name(found_prot)); node *session_privates = node_GetItemByKey(pss->session,"privates"); set_obj_int(session_privates,"is_http",1); //printf("created new session :%d actual sessions num:%d\n",lsession_uid,lsessions_num); } node *parameters = create_obj("parameters"); node *base_class = get_base_class(state); node *prot_value = create_class_instance(base_class); set_obj_string(prot_value,"name","protocol"); set_obj_string(prot_value,"value",get_obj_name(found_prot)); node_AddItem(parameters,prot_value); inc_obj_refcount(prot_value); char *url = str_CreateEmpty(); url = str_AddChars(url,in,len); node *url_value = create_class_instance(base_class); set_obj_string(url_value,"name","url"); set_obj_string(url_value,"value",url); node_AddItem(parameters,url_value); inc_obj_refcount(url_value); free(url); node_AddItem(parameters,pss->session); inc_obj_refcount(pss->session); //node_AddItem(parameters,daemon_obj); node_AddItem(parameters,sessions); inc_obj_refcount(sessions); node *tmp_parent = node_GetParent(found_prot); node *bmembers = node_GetItemByKey(block,"members"); node_SetParent(found_prot,bmembers); node *ret_obj = execute_obj(state,found_prot,block,parameters,True,False);//,True);resolve node_SetParent(found_prot,tmp_parent); //dec_obj_refcount(msg_value); dec_obj_refcount(prot_value); //add_garbage(state,msg_value);//TODO check if "just survives" add_garbage(state,prot_value); dec_obj_refcount(url_value); add_garbage(state,url_value); dec_obj_refcount(pss->session); dec_obj_refcount(sessions); node *ret_obj_value = node_GetItemByKey(ret_obj,"value"); if( (node_GetType(ret_obj_value)==NODE_TYPE_STRING && strlen(node_GetString(ret_obj_value))) || (node_GetType(ret_obj_value)==NODE_TYPE_BINARY && node_GetBinaryLength(ret_obj_value)) ) { //printf("returning http message: [%s] :%d\n",node_GetString(ret_obj_value),strlen(node_GetString(ret_obj_value))); //node *ret_obj_copy = node_CopyTree(ret_obj,True,True); node *ret_obj_copy = copy_class(ret_obj); //reset_obj_refcount(ret_obj_copy); set_obj_string(ret_obj_copy,"name","message"); add_member(pss->session,ret_obj_copy); inc_obj_refcount(ret_obj_copy); } libwebsocket_callback_on_writable(context, wsi); } break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: if(found_prot) { printf("found prot in http body complete : %d,num:%d\n",lsession_uid,lsessions_num); if(daemon_obj) { printf("body: found daemon_obj\n"); } } else printf("body closed: prot not found\n"); //lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); libwebsockets_return_http_status(context,wsi,HTTP_STATUS_OK,NULL); return(-1); case LWS_CALLBACK_HTTP_FILE_COMPLETION: if(found_prot) { //printf("found prot in http file complete : %d,num:%d\n",lsession_uid,lsessions_num); lsessions_num--; node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); node_SetSint32(sessions_num_value,lsessions_num); delete_session(state,sessions,pss->session); pss->session = NULL; if(daemon_obj) { printf("http: found daemon_obj\n"); } } else printf("file closed: prot not found\n"); return(-1); case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: if(found_prot) { int n; static const char *token_names[] = { /*[WSI_TOKEN_GET_URI] =*/ "GET URI", /*[WSI_TOKEN_POST_URI] =*/ "POST URI", /*[WSI_TOKEN_OPTIONS] =*/ "Options", /*[WSI_TOKEN_HOST] =*/ "Host", /*[WSI_TOKEN_CONNECTION] =*/ "Connection", /*[WSI_TOKEN_KEY1] =*/ "key 1", /*[WSI_TOKEN_KEY2] =*/ "key 2", /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol", /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade", /*[WSI_TOKEN_ORIGIN] =*/ "Origin", /*[WSI_TOKEN_DRAFT] =*/ "Draft", /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge", /* new for 04 */ /*[WSI_TOKEN_KEY] =*/ "Key", /*[WSI_TOKEN_VERSION] =*/ "Version", /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin", /* new for 05 */ /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions", /* client receives these */ /*[WSI_TOKEN_ACCEPT] =*/ "Accept", /*[WSI_TOKEN_NONCE] =*/ "Nonce", /*[WSI_TOKEN_HTTP] =*/ "Http", "Accept:", "Accept_Request_Headers:", "If-None-Match:", "If-Modified-Since:", "Accept-Encoding:", "Accept-Language:", "Pragma:", "Cache-Control:", "Authorization:", "Cookie:", "Content-Length:", "Content-Type:", "Date:", "Range:", "Referer:", "Uri-Args:", /*[WSI_TOKEN_MUXURL] =*/ "MuxURL", }; //printf("found prot in http filter callback : uid:%d,num:%d (sess:%x)\n",lsession_uid+1,lsessions_num,pss->session); if(!pss->session) { lsession_uid++; node *session_uid_value = node_GetItemByKey(session_uid,"value"); node_SetSint32(session_uid_value,lsession_uid); lsessions_num++; node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); node_SetSint32(sessions_num_value,lsessions_num); pss->session = create_session(state,sessions,lsession_uid,get_obj_name(found_prot)); //node *session_privates = node_GetItemByKey(pss->session,"privates"); //set_obj_int(session_privates,"is_http",1); } //printf("filter sess:%x\n",pss->session); for(n=0;n<(int)(sizeof(token_names)/sizeof(token_names[0]));n++) { if (!lws_hdr_total_length(wsi, n)) continue; char *cookies = (char*)malloc(512); memset(cookies,0,512); lws_hdr_copy(wsi,cookies,511,n); //printf("header:%s = [%s]\n",token_names[n],cookies); //fflush(stdout); if(pss->session && !strcmp("Cookie:",token_names[n])) { //printf("cookie found:%s = [%s]\n",token_names[n],cookies); //fflush(stdout); node *base_class = get_base_class(state); node *cookie_value = create_class_instance(base_class); set_obj_string(cookie_value,"name","cookie"); set_obj_string(cookie_value,"value",cookies); add_member(pss->session,cookie_value); inc_obj_refcount(cookie_value); } free(cookies); } } break; case LWS_CALLBACK_HTTP_WRITEABLE: case LWS_CALLBACK_SERVER_WRITEABLE: { //node_PrintTree(pss->session); node *message = get_member(pss->session,"message"); node *session_privates = node_GetItemByKey(pss->session,"privates"); node *http_only = node_GetItemByKey(session_privates,"is_http"); while(message) { //node *session_id = get_member(pss->session,"id"); //node *session_id_value = node_GetItemByKey(session_id,"value"); node *message_value = node_GetItemByKey(message,"value"); unsigned char *me = NULL; unsigned long me_len = 0; if(node_GetType(message_value)==NODE_TYPE_STRING) { me = (unsigned char*)node_GetString(message_value); me_len = strlen((char*)me); } else if(node_GetType(message_value)==NODE_TYPE_BINARY) { me = (unsigned char*)node_GetBinary(message_value); me_len = node_GetBinaryLength(message_value); } //printf("sending message now: [%s] to: %d\n",me,node_GetSint32(session_id_value)); //fflush(stdout); unsigned char *buf = (unsigned char*)malloc(LWS_SEND_BUFFER_PRE_PADDING + me_len + LWS_SEND_BUFFER_POST_PADDING); memcpy(buf+LWS_SEND_BUFFER_PRE_PADDING,me,me_len); if(http_only) //n = libwebsocket_write(wsi, me, me_len, LWS_WRITE_HTTP); n = libwebsocket_write(wsi,buf+LWS_SEND_BUFFER_PRE_PADDING,me_len,LWS_WRITE_HTTP); else //n = libwebsocket_write(wsi, me, me_len, LWS_WRITE_TEXT); n = libwebsocket_write(wsi,buf+LWS_SEND_BUFFER_PRE_PADDING,me_len,LWS_WRITE_TEXT); free(buf); if(n<0) { printf("ERROR %d writing to socket, hanging up\n", n); return(1); } if(n<(long)me_len) { printf("Partial write\n"); return(-1); } //node_FreeTree(pss->message); remove_member(pss->session,message); dec_obj_refcount(message); //printf("removing message from queue:%x (%d)\n",message,get_obj_refcount(message)); add_garbage(state,message); message = get_member(pss->session,"message"); } if(http_only) { //if(lws_http_transaction_completed(wsi)) //{ //printf("removing http session num:%d\n",lsessions_num); lsessions_num--; node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); node_SetSint32(sessions_num_value,lsessions_num); delete_session(state,sessions,pss->session); pss->session = NULL; //printf("removed http\n"); return -1; //return(-1); //} //else // libwebsocket_callback_on_writable(context, wsi); } } break; case LWS_CALLBACK_ESTABLISHED: if(found_prot) { //printf("found prot in establish callback : uid:%d,num:%d (sess:%x)\n",lsession_uid+1,lsessions_num,pss->session); if(!pss->session) { lsession_uid++; node *session_uid_value = node_GetItemByKey(session_uid,"value"); node_SetSint32(session_uid_value,lsession_uid); lsessions_num++; node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); node_SetSint32(sessions_num_value,lsessions_num); pss->session = create_session(state,sessions,lsession_uid,get_obj_name(found_prot)); } if(daemon_obj) { node *connect_handler = get_member(daemon_obj,"connect_handler"); if(connect_handler) { connect_handler = resolve_object(state,connect_handler); node *parameters = create_obj("parameters"); node *base_class = get_base_class(state); node *prot_value = create_class_instance(base_class); set_obj_string(prot_value,"name","protocol"); set_obj_string(prot_value,"value",get_obj_name(found_prot)); node_AddItem(parameters,prot_value); inc_obj_refcount(prot_value); node_AddItem(parameters,pss->session); inc_obj_refcount(pss->session); node_AddItem(parameters,sessions); inc_obj_refcount(sessions); node *tmp_parent = node_GetParent(connect_handler); node *bmembers = node_GetItemByKey(block,"members"); node_SetParent(connect_handler,bmembers); node *ret_obj = execute_obj(state,connect_handler,block,parameters,True,False);//,True);resolve node_SetParent(connect_handler,tmp_parent); dec_obj_refcount(prot_value); add_garbage(state,prot_value); dec_obj_refcount(pss->session); dec_obj_refcount(sessions); node *ret_obj_value = node_GetItemByKey(ret_obj,"value"); if(node_GetType(ret_obj_value)==NODE_TYPE_STRING && strlen(node_GetString(ret_obj_value))) { } } } } break; case LWS_CALLBACK_CLOSED_HTTP: break; case LWS_CALLBACK_CLOSED: case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: if(found_prot) { //printf("found prot in closed callback : uid:%d,num:%d (sess:%x)\n",lsession_uid,lsessions_num,pss->session); if(daemon_obj) { //printf("closed: found daemon_obj\n"); node *disconnect_handler = get_member(daemon_obj,"disconnect_handler"); if(disconnect_handler) { //printf("disc found\n"); disconnect_handler = resolve_object(state,disconnect_handler); node *parameters = create_obj("parameters"); node *base_class = get_base_class(state); node *prot_value = create_class_instance(base_class); set_obj_string(prot_value,"name","protocol"); set_obj_string(prot_value,"value",get_obj_name(found_prot)); node_AddItem(parameters,prot_value); inc_obj_refcount(prot_value); node_AddItem(parameters,pss->session); inc_obj_refcount(pss->session); node_AddItem(parameters,sessions); inc_obj_refcount(sessions); node *tmp_parent = node_GetParent(disconnect_handler); node *bmembers = node_GetItemByKey(block,"members"); node_SetParent(disconnect_handler,bmembers); node *ret_obj = execute_obj(state,disconnect_handler,block,parameters,True,False);//,True);resolve node_SetParent(disconnect_handler,tmp_parent); dec_obj_refcount(prot_value); add_garbage(state,prot_value); dec_obj_refcount(pss->session); dec_obj_refcount(sessions); node *ret_obj_value = node_GetItemByKey(ret_obj,"value"); if(node_GetType(ret_obj_value)==NODE_TYPE_STRING && strlen(node_GetString(ret_obj_value))) { } } } lsessions_num--; node *sessions_num_value = node_GetItemByKey(sessions_num,"value"); node_SetSint32(sessions_num_value,lsessions_num); delete_session(state,sessions,pss->session); pss->session = NULL; //printf("disconnected\n"); } else { printf("closed connection without prot found\n"); if(pss->session) printf("but a session was found\n"); } break; case LWS_CALLBACK_RECEIVE: if(len>1024) { //TODO use some variable lwsl_err("Server received packet bigger than %u, hanging up\n", 1024); return(1); } if(found_prot) { node *parameters = create_obj("parameters"); node *base_class = get_base_class(state); node *prot_value = create_class_instance(base_class); set_obj_string(prot_value,"name","protocol"); set_obj_string(prot_value,"value",get_obj_name(found_prot)); node_AddItem(parameters,prot_value); inc_obj_refcount(prot_value); char *msg = str_CreateEmpty(); msg = str_AddChars(msg,in,len); node *msg_value = create_class_instance(base_class); set_obj_string(msg_value,"name","message"); set_obj_string(msg_value,"value",msg); node_AddItem(parameters,msg_value); inc_obj_refcount(msg_value); free(msg); /*node *session_value = create_class_instance(base_class); reset_obj_refcount(session_value); add_garbage(state,session_value); set_obj_string(session_value,"name","session_id"); set_obj_int(session_value,"value",lsession_uid); set_obj_int(session_value,"item_index",2); node_AddItem(parameters,session_value); */ node_AddItem(parameters,pss->session); inc_obj_refcount(pss->session); //node_AddItem(parameters,daemon_obj); //inc_obj_refcount(daemon_obj); node_AddItem(parameters,sessions); inc_obj_refcount(sessions); //printf("recv callback\n"); //fflush(stdout); node *tmp_parent = node_GetParent(found_prot); node *bmembers = node_GetItemByKey(block,"members"); node_SetParent(found_prot,bmembers); node *ret_obj = execute_obj(state,found_prot,block,parameters,True,False);//,True);resolve node_SetParent(found_prot,tmp_parent); //printf("recv callback finished\n"); //fflush(stdout); dec_obj_refcount(msg_value); dec_obj_refcount(prot_value); add_garbage(state,msg_value);//TODO check if "just survives" add_garbage(state,prot_value); dec_obj_refcount(pss->session); dec_obj_refcount(sessions); //dec_obj_refcount(daemon_obj); //printf("recv gc\n"); //fflush(stdout); //node *ret_obj_value = node_GetItemByKey(ret_obj,"value"); //char *me = node_GetString(ret_obj_value); //printf("returned string:[%s]\n",me); node *ret_obj_value = node_GetItemByKey(ret_obj,"value"); if(node_GetType(ret_obj_value)==NODE_TYPE_STRING && strlen(node_GetString(ret_obj_value))) { //printf("returning message: [%s] :%d\n",node_GetString(ret_obj_value),strlen(node_GetString(ret_obj_value))); //node *ret_obj_copy = node_CopyTree(ret_obj,True,True); node *ret_obj_copy = copy_class(ret_obj); //reset_obj_refcount(ret_obj_copy); set_obj_string(ret_obj_copy,"name","message"); add_member(pss->session,ret_obj_copy); inc_obj_refcount(ret_obj_copy); //set_obj_string(ret_obj,"name","message"); //add_member(pss->session,ret_obj); //inc_obj_refcount(ret_obj); } libwebsocket_callback_on_writable(context, wsi); } break; default: break; } return(0); }
static int callback_http(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { #if 0 char client_name[128]; char client_ip[128]; #endif char buf[256]; char leaf_path[1024]; char b64[64]; struct timeval tv; int n, m; unsigned char *p; char *other_headers = 0; static unsigned char buffer[4096]; struct stat stat_buf; struct per_session_data__http *pss = (struct per_session_data__http *)user; const char *mimetype; #ifdef EXTERNAL_POLL struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in; #endif const string path = WEBUI_PATH; const char* resource_path = path.c_str(); switch (reason) { case LWS_CALLBACK_HTTP: dump_handshake_info(wsi); if (len < 1) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL); return -1; } /* this server has no concept of directories */ if (strchr((const char *)in + 1, '/')) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); return -1; } /* if a legal POST URL, let it continue and accept data */ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) return 0; /* check for the "send a big file by hand" example case */ if (!strcmp((const char *)in, "/leaf.jpg")) { if (strlen(resource_path) > sizeof(leaf_path) - 10) return -1; sprintf(leaf_path, "%s/leaf.jpg", resource_path); /* well, let's demonstrate how to send the hard way */ p = buffer; #ifdef WIN32 pss->fd = open(leaf_path, O_RDONLY | _O_BINARY); #else pss->fd = open(leaf_path, O_RDONLY); #endif if (pss->fd < 0) return -1; fstat(pss->fd, &stat_buf); /* * we will send a big jpeg file, but it could be * anything. Set the Content-Type: appropriately * so the browser knows what to do with it. */ p += sprintf((char *)p, "HTTP/1.0 200 OK\x0d\x0a" "Server: libwebsockets\x0d\x0a" "Content-Type: image/jpeg\x0d\x0a" "Content-Length: %u\x0d\x0a\x0d\x0a", (unsigned int)stat_buf.st_size); /* * send the http headers... * this won't block since it's the first payload sent * on the connection since it was established * (too small for partial) */ n = libwebsocket_write(wsi, buffer, p - buffer, LWS_WRITE_HTTP); if (n < 0) { close(pss->fd); return -1; } /* * book us a LWS_CALLBACK_HTTP_WRITEABLE callback */ libwebsocket_callback_on_writable(context, wsi); break; } /* if not, send a file the easy way */ // FIXME: Data loss if buffer is too small strncpy(buf, resource_path, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; if (strcmp((const char*)in, "/")) { if (*((const char *)in) != '/') strcat(buf, "/"); strncat(buf, (const char*)in, sizeof(buf) - strlen(resource_path)); } else /* default file to serve */ strcat(buf, "/debugger.html"); buf[sizeof(buf) - 1] = '\0'; /* refuse to serve files we don't understand */ mimetype = get_mimetype(buf); if (!mimetype) { lwsl_err("Unknown mimetype for %s\n", buf); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL); return -1; } if (libwebsockets_serve_http_file(context, wsi, buf, mimetype, other_headers)) return -1; /* through completion or error, close the socket */ /* * notice that the sending of the file completes asynchronously, * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when * it's done */ break; case LWS_CALLBACK_HTTP_BODY: strncpy(buf, (const char*)in, 20); buf[20] = '\0'; if (len < 20) buf[len] = '\0'; lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", (const char *)buf, (int)len); break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); /* the whole of the sent body arried, close the connection */ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_OK, NULL); return -1; case LWS_CALLBACK_HTTP_FILE_COMPLETION: // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); /* kill the connection after we sent one file */ return -1; case LWS_CALLBACK_HTTP_WRITEABLE: /* * we can send more of whatever it is we were sending */ do { n = read(pss->fd, buffer, sizeof buffer); /* problem reading, close conn */ if (n < 0) goto bail; /* sent it all, close conn */ if (n == 0) goto flush_bail; /* * because it's HTTP and not websocket, don't need to take * care about pre and postamble */ m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP); if (m < 0) /* write failed, close conn */ goto bail; if (m != n) /* partial write, adjust */ lseek(pss->fd, m - n, SEEK_CUR); if (m) /* while still active, extend timeout */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5); } while (!lws_send_pipe_choked(wsi)); libwebsocket_callback_on_writable(context, wsi); break; flush_bail: /* true if still partial pending */ if (lws_send_pipe_choked(wsi)) { libwebsocket_callback_on_writable(context, wsi); break; } bail: close(pss->fd); return -1; /* * callback for confirming to continue with client IP appear in * protocol 0 callback since no websocket protocol has been agreed * yet. You can just ignore this if you won't filter on client IP * since the default uhandled callback return is 0 meaning let the * connection continue. */ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: #if 0 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name, sizeof(client_name), client_ip, sizeof(client_ip)); fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip); #endif /* if we returned non-zero from here, we kill the connection */ break; #ifdef EXTERNAL_POLL /* * callbacks for managing the external poll() array appear in * protocol 0 callback */ case LWS_CALLBACK_LOCK_POLL: /* * lock mutex to protect pollfd state * called before any other POLL related callback */ break; case LWS_CALLBACK_UNLOCK_POLL: /* * unlock mutex to protect pollfd state when * called after any other POLL related callback */ break; case LWS_CALLBACK_ADD_POLL_FD: if (count_pollfds >= max_poll_elements) { lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n"); return 1; } fd_lookup[pa->fd] = count_pollfds; pollfds[count_pollfds].fd = pa->fd; pollfds[count_pollfds].events = pa->events; pollfds[count_pollfds++].revents = 0; break; case LWS_CALLBACK_DEL_POLL_FD: if (!--count_pollfds) break; m = fd_lookup[pa->fd]; /* have the last guy take up the vacant slot */ pollfds[m] = pollfds[count_pollfds]; fd_lookup[pollfds[count_pollfds].fd] = m; break; case LWS_CALLBACK_CHANGE_MODE_POLL_FD: pollfds[fd_lookup[pa->fd]].events = pa->events; break; #endif case LWS_CALLBACK_GET_THREAD_ID: /* * if you will call "libwebsocket_callback_on_writable" * from a different thread, return the caller thread ID * here so lws can use this information to work out if it * should signal the poll() loop to exit and restart early */ /* return pthread_getthreadid_np(); */ break; default: break; } return 0; }
static int callback_http(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { (void)user; char buf[256]; char leaf_path[1024]; char b64[64]; struct timeval tv; char *other_headers; const char *mimetype; switch (reason) { case LWS_CALLBACK_HTTP: dump_handshake_info(wsi); if (len < 1) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL); return -1; } /* this server has no concept of directories */ if (strchr((const char *)in + 1, '/')) { libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); return -1; } /* if a legal POST URL, let it continue and accept data */ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) return 0; /* if not, send a file the easy way */ strcpy(buf, resource_path); if (strcmp((const char*)in, "/")) { strcat(buf, "/"); strncat(buf, (char*)in, sizeof(buf) - strlen(resource_path)); }else{ /* default file to serve */ strcat(buf, "/monitor.html"); } buf[sizeof(buf) - 1] = '\0'; /* refuse to serve files we don't understand */ mimetype = get_mimetype(buf); if (!mimetype) { lwsl_err("Unknown mimetype for %s\n", buf); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL); return -1; } /* demostrates how to set a cookie on / */ other_headers = NULL; if (!strcmp((const char *)in, "/") && !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { /* this isn't very unguessable but it'll do for us */ gettimeofday(&tv, NULL); sprintf(b64, "LWS_%u_%u_COOKIE", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec); sprintf(leaf_path, "Set-Cookie: test=LWS_%u_%u_COOKIE;Max-Age=360000\x0d\x0a", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec); other_headers = leaf_path; lwsl_err(other_headers); } if (libwebsockets_serve_http_file(context, wsi, buf, mimetype, other_headers)){ return -1; /* through completion or error, close the socket */ } break; case LWS_CALLBACK_HTTP_BODY: strncpy(buf, (char*)in, 20); buf[20] = '\0'; if (len < 20) buf[len] = '\0'; lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", (const char *)buf, (int)len); break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); /* the whole of the sent body arried, close the connection */ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_OK, NULL); return -1; case LWS_CALLBACK_HTTP_FILE_COMPLETION: /* kill the connection after we sent one file */ return -1; case LWS_CALLBACK_HTTP_WRITEABLE: return -1; /* * callback for confirming to continue with client IP appear in * protocol 0 callback since no websocket protocol has been agreed * yet. You can just ignore this if you won't filter on client IP * since the default uhandled callback return is 0 meaning let the * connection continue. */ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: #if 0 libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name, sizeof(client_name), client_ip, sizeof(client_ip)); fprintf(stderr, "Received network connect from %s (%s)\n", client_name, client_ip); #endif /* if we returned non-zero from here, we kill the connection */ break; case LWS_CALLBACK_GET_THREAD_ID: /* * if you will call "libwebsocket_callback_on_writable" * from a different thread, return the caller thread ID * here so lws can use this information to work out if it * should signal the poll() loop to exit and restart early */ /* return pthread_getthreadid_np(); */ break; default: break; } return 0; }