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
}
Beispiel #2
0
 //--------------------------------------------------------------
 unsigned int Reactor::_notify(Connection* conn,
                             enum libwebsocket_callback_reasons const reason,
                             const char* const _message,
                             const unsigned int len){
     if (conn == NULL || conn->protocol == NULL){
         if (conn == NULL){
             ofLog(OF_LOG_WARNING, "connection is null");
         } else {
             ofLog(OF_LOG_WARNING, "protocol is null"); 
         }
         return 1;
     }
     
     std::string message;
     
     if (_message != NULL && len > 0){
         message = std::string(_message, len);
     }
     
     // decide if this is part of a larger message or not
     size_t bytesLeft = libwebsockets_remaining_packet_payload( conn->ws );
     if ( !bReceivingLargeMessage && bytesLeft > 0 ){
         bReceivingLargeMessage = true;
     }
     bool bFinishedReceiving = false;
     
     if ( bReceivingLargeMessage){
         largeMessage += message;
         if ( bytesLeft == 0 ){
             message = largeMessage;
             bFinishedReceiving      = true;
             bReceivingLargeMessage  = false;
             largeMessage = "";
         }
     }
     
     Event args(*conn, message);
     
     if (_message != NULL && len > 0 && (!bReceivingLargeMessage || bFinishedReceiving) ){
         args.json = Json::Value( Json::nullValue );
         
         args.message = args.conn.recv(args.message);
         
         bool parsingSuccessful = ( bParseJSON ? reader.parse( args.message, args.json ) : false);
         if ( !parsingSuccessful ){
             // report to the user the failure and their locations in the document.
             ofLog( OF_LOG_VERBOSE, "Failed to parse JSON\n"+ reader.getFormatedErrorMessages() );
             args.json = Json::Value( Json::nullValue );
         }
     }
     
     if (reason==LWS_CALLBACK_ESTABLISHED || reason == LWS_CALLBACK_CLIENT_ESTABLISHED){
         connections.push_back( conn );
         ofNotifyEvent(conn->protocol->onopenEvent, args);
     } else if (reason==LWS_CALLBACK_CLOSED){
         
         // erase connection from vector
         for (int i=0; i<connections.size(); i++){
             if ( connections[i] == conn ){
                 connections.erase( connections.begin() + i );
                 break;
             }
         }
         
         ofNotifyEvent(conn->protocol->oncloseEvent, args);
     } else if (reason==LWS_CALLBACK_SERVER_WRITEABLE){
         ofNotifyEvent(conn->protocol->onidleEvent, args);
         
         // only notify if we have a complete message
     } else if (reason==LWS_CALLBACK_BROADCAST && (!bReceivingLargeMessage || bFinishedReceiving) ){
         ofNotifyEvent(conn->protocol->onbroadcastEvent, args);
         
     // only notify if we have a complete message
     } else if ((reason==LWS_CALLBACK_RECEIVE || reason == LWS_CALLBACK_CLIENT_RECEIVE) && (!bReceivingLargeMessage || bFinishedReceiving)){
         ofNotifyEvent(conn->protocol->onmessageEvent, args);
     }
     
     return 0;
 }
Beispiel #3
0
int bmx_libwebsockets_remaining_packet_payload(struct libwebsocket * wsi) {
	return static_cast<int>(libwebsockets_remaining_packet_payload(wsi));
}
    //--------------------------------------------------------------
    unsigned int Reactor::_notify(Connection* conn,
                                enum libwebsocket_callback_reasons const reason,
                                const char* const _message,
                                const unsigned int len){
        
        // this happens with events that don't use the connection
        // so not always a problem
        if (conn == NULL || conn->protocol == NULL || conn->ws == NULL ){
            if (conn == NULL){
                ofLog(OF_LOG_WARNING, "[ofxLibwebsockets] connection is null ");
            } else {
                ofLog(OF_LOG_WARNING, "[ofxLibwebsockets] protocol is null");
            }
            return 1;
        }

        if (closeAndFree){
          closeAndFree = false;
          return -1;
        }
        
        std::string message;
        Event args(*conn, message);
        
        switch (reason) {
            // connection was not successful
            case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
                ofLogError()<<"[ofxLibwebsockets] Connection error";
                
                for (int i=0; i<connections.size(); i++){
                    if ( connections[i] == conn ){
                        connections.erase( connections.begin() + i );
                        break;
                    }
                }
                ofNotifyEvent(conn->protocol->oncloseEvent, args);
                break;
                
            // last thing that happens before connection goes dark
            case LWS_CALLBACK_WSI_DESTROY:
            {
                bool bFound = false;
                for (int i=0; i<connections.size(); i++){
                    if ( connections[i] == conn ){
                        bFound = true; // valid connection
                        connections.erase( connections.begin() + i );
                        break;
                    }
                }
                
                if ( bFound ) ofNotifyEvent(conn->protocol->oncloseEvent, args);
            }
                break;
            
            case LWS_CALLBACK_ESTABLISHED:          // server connected with client
            case LWS_CALLBACK_CLIENT_ESTABLISHED:   // client connected with server
                connections.push_back( conn );
                ofNotifyEvent(conn->protocol->onopenEvent, args);
                break;
                
            case LWS_CALLBACK_CLOSED:
                // erase connection from vector
                for (int i=0; i<connections.size(); i++){
                    if ( connections[i] == conn ){
                        connections.erase( connections.begin() + i );
                        break;
                    }
                }
                
                ofNotifyEvent(conn->protocol->oncloseEvent, args);
                break;
                
            case LWS_CALLBACK_SERVER_WRITEABLE:
            case LWS_CALLBACK_CLIENT_WRITEABLE:
                // idle is good! means you can write again
                conn->setIdle();
                break;
                
            case LWS_CALLBACK_RECEIVE:              // server receive
            case LWS_CALLBACK_CLIENT_RECEIVE:       // client receive
            case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
                {
                    
                    bool bFinishedReceiving = false;
                    
                    // decide if this is part of a larger message or not
                    size_t bytesLeft = libwebsockets_remaining_packet_payload( conn->ws );
                    
                    if ( !bReceivingLargeMessage && (bytesLeft > 0 || !libwebsocket_is_final_fragment( conn->ws )) ){
                        bReceivingLargeMessage = true;
                    }
                    
                    // text or binary?
                    int isBinary = lws_frame_is_binary(conn->ws);
                    
                    if (isBinary == 1 ){
                        // set binary flag on event
                        args.isBinary = true;
                        
                        if ( bReceivingLargeMessage){
                            // need to allocate data...
                            if ( largeBinarySize == 0 ){
                                largeBinaryMessage.set( _message, len );
                                largeBinarySize = len;
                            } else {
                                largeBinarySize += len;
                                largeBinaryMessage.append(_message, len);
                            }
                            
                            if ( bytesLeft == 0 && libwebsocket_is_final_fragment( conn->ws )){
                                // copy into event
                                args.data.set(largeBinaryMessage.getData(), largeBinaryMessage.size());
                                
                                bFinishedReceiving      = true;
                                bReceivingLargeMessage  = false;
                                largeBinaryMessage.clear();
                                largeBinarySize = 0;
                            }
                        } else {
                            args.data.set(_message, len);
                            
                            bFinishedReceiving      = true;
                            bReceivingLargeMessage  = false;
                            largeBinaryMessage.clear();
                            largeBinarySize = 0;
                        }
                    } else {
                        if (_message != NULL && len > 0){
                            args.message = std::string(_message, len);
                        }
                        
                        if ( bReceivingLargeMessage){
                            largeMessage += args.message;
                            if ( bytesLeft == 0 && libwebsocket_is_final_fragment( conn->ws )){
                                args.message = largeMessage;
                                bFinishedReceiving      = true;
                                bReceivingLargeMessage  = false;
                                largeMessage = "";
                            }
                        }
                        
                        if (_message != NULL && len > 0 && (!bReceivingLargeMessage || bFinishedReceiving) ){
                            args.json = Json::Value( Json::nullValue );
                            
                            bool parsingSuccessful = ( bParseJSON ? reader.parse( args.message, args.json ) : false);
                            if ( !parsingSuccessful ){
                                // report to the user the failure and their locations in the document.
                                ofLog( OF_LOG_VERBOSE, "[ofxLibwebsockets] Failed to parse JSON\n"+ reader.getFormatedErrorMessages() );
                                args.json = Json::Value( Json::nullValue );
                            }
                        }
                    }
                    
                    // only notify if we have a complete message
                    if (!bReceivingLargeMessage || bFinishedReceiving){
                        ofNotifyEvent(conn->protocol->onmessageEvent, args);
                    }
                }
                break;
                
            default:
                ofLogVerbose() << "[ofxLibwebsockets] received unknown event "<< reason <<endl;
                break;
        }
        
        return 0;
    }
Beispiel #5
0
int WebSocketClient2::lws_callback_vopp(struct libwebsocket_context * ctx,
                                        struct libwebsocket *wsi,
                                        enum libwebsocket_callback_reasons reason,
                                        void *user, void *in, size_t len)
{
	unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
                      LWS_SEND_BUFFER_POST_PADDING];
	int l;
    int n;
    std::string str1, str2;

    struct client_session_user_data *ssd = (struct client_session_user_data*)user;
    WebSocketClient2 *wsc = (WebSocketClient2*)(libwebsocket_context_user_data(ctx));

	switch (reason) {
	case LWS_CALLBACK_CLOSED:
		fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED\n");
        if (ssd->pc != NULL) {
            delete ssd->pc; ssd->pc = NULL;
        }
        wsc->lws_connection_closed(wsi);
		break;

	case LWS_CALLBACK_CLIENT_ESTABLISHED:
        // 因为在调用libwebsocket_client_connect的时候,事件循环还没有启动,
        // 所以事件不会传递出来。
        qLogx()<<"connection state:"
               <<libwebsockets_remaining_packet_payload(wsi)
               <<libwebsocket_get_socket_fd(wsi)
               <<libwebsocket_get_connect_state(wsi);

        ssd->pc = new PackageCombiner;

        wsc->lws_new_connection_established(wsi);
		/*
		 * start the ball rolling,
		 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
		 */

		// libwebsocket_callback_on_writable(ctx, wsi);
		break;

	case LWS_CALLBACK_CLIENT_RECEIVE:
        /*		fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */
        // wsc->lws_ws_message_ready(wsi, (char*)in, len);

        str1 = std::string((char*)in, len);
        ssd->pc->save_fragment(str1);
        if (ssd->pc->is_package_finish(str1)) {
            str2 = ssd->pc->get_full_package(str1);
            strncpy(ssd->full_message, str2.c_str(), sizeof(ssd->full_message)-1);
            n = wsc->lws_ws_message_ready(wsi, ssd->full_message, str2.length());
        }

		break;

	case LWS_CALLBACK_CLIENT_WRITEABLE:
		// l = sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING],
		// 			"c #%06X %d %d %d;",
		// 			(int)random() & 0xffffff,
		// 			(int)random() % 500,
		// 			(int)random() % 250,
		// 			(int)random() % 24);

		// libwebsocket_write(wsi,
        //                    &buf[LWS_SEND_BUFFER_PRE_PADDING], l, LWS_WRITE_TEXT);

		// /* get notified as soon as we can write again */

		// libwebsocket_callback_on_writable(ctx, wsi);
        libwebsocket_callback_on_writable(ctx, wsi);
		// /*
		//  * Without at least this delay, we choke the browser
		//  * and the connection stalls, despite we now take care about
		//  * flow control
		//  */

		// usleep(200);
		break;

	default:
		break;
	}

    return 0;
}
Beispiel #6
0
bool WebSocketClient2::on_wsctx_inited()
{
    QUrl mu(this->m_uri);
    qLogx()<<mu;

    unsigned short port = mu.port(18080);
    QString host = mu.host();
    QString path = mu.path();

    // 默认为1,最安全
    int ssl_mode = 1; // 0,ws://, 1,wss://encrypt, 2,wss://self signed

    if (mu.scheme().toLower() == "ws") {
        ssl_mode = 0;
    } else if (mu.scheme().toLower() == "wss") {
        ssl_mode = 2;
    } else {
        Q_ASSERT(1==2);
    }

    int conn_retry = 3;
    while (conn_retry -- > 0) {
        // 需要关注测试一下,这个调用是否是阻塞式的,如果是非阻塞式,则可以这么用
        // 否则,还需要考虑其他解决方式。
        qLogx()<<this->m_uri<<host<<path<<port;
        qLogx()<<"Before libwss clint connect..."<<QDateTime::currentDateTime();
        this->m_wsi = libwebsocket_client_connect(this->m_lws_ctx, host.toAscii().data(), port, ssl_mode,
                                                  path.toAscii().data(), 
                                                  host.toAscii().data(), host.toAscii().data(),
                                                  vopp_client_protocols[0].name, -1);
        // this->m_wsi = libwebsocket_client_connect(this->m_lws_ctx, host.toAscii().data(), port, 0,
        //                                           path.toAscii().data(), 
        //                                           host.toAscii().data(), host.toAscii().data(),
        //                                           vopp_client_protocols[0].name, -1);

        if (this->m_wsi == NULL) {
            qLogx()<<"libwebsocket dumb connect failed.";
            return false;
        }

        // here the conn state is 4, but when run to CALLBACK_ESTABLISH, the state will be 3 ok
        qLogx()<<"After libwss client connect..."<<QDateTime::currentDateTime()
               <<libwebsockets_remaining_packet_payload(this->m_wsi)
               <<libwebsocket_get_socket_fd(this->m_wsi)
               <<libwebsocket_get_connect_state(this->m_wsi);
        
        qLogx()<<"client wsi:"<<this->m_wsi;
        if (libwebsocket_client_is_connected(this->m_wsi)) {
            break;
        } else {
            break;
            this->m_wsi = NULL;
            qLogx()<<"Invalid client wsi state, retry.. "<<conn_retry;
            if (conn_retry == 0) {
                Q_ASSERT(1==2);
                return false;
            }
        }
    }
    return true;
}
Beispiel #7
0
int WebSocket::onSocketCallback(struct libwebsocket_context *ctx,
                     struct libwebsocket *wsi,
                     int reason,
                     void *user, void *in, ssize_t len)
{
	//CCLOG("socket callback for %d reason", reason);
    CCAssert(_wsContext == nullptr || ctx == _wsContext, "Invalid context.");
    CCAssert(_wsInstance == nullptr || wsi == nullptr || wsi == _wsInstance, "Invaild websocket instance.");

	switch (reason)
    {
        case LWS_CALLBACK_DEL_POLL_FD:
        case LWS_CALLBACK_PROTOCOL_DESTROY:
        case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
            {
                WsMessage* msg = nullptr;
                if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR
                    || (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == State::kStateConnecting)
                    || (reason == LWS_CALLBACK_DEL_POLL_FD && _readyState == State::kStateConnecting)
                    )
                {
                    msg = new WsMessage();
                    msg->what = WS_MSG_TO_UITHREAD_ERROR;
                    _readyState = State::kStateClosing;
                }
                else if (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == State::kStateClosing)
                {
                    msg = new WsMessage();
                    msg->what = WS_MSG_TO_UITHREAD_CLOSE;
                }

                if (msg)
                {
                    _wsHelper->sendMessageToUIThread(msg);
                }
            }
            break;
        case LWS_CALLBACK_CLIENT_ESTABLISHED:
            {
                WsMessage* msg = new WsMessage();
                msg->what = WS_MSG_TO_UITHREAD_OPEN;
                _readyState = State::kStateOpen;
                
                /*
                 * start the ball rolling,
                 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
                 */
                libwebsocket_callback_on_writable(ctx, wsi);
                _wsHelper->sendMessageToUIThread(msg);
            }
            break;
            
        case LWS_CALLBACK_CLIENT_WRITEABLE:
            {

                std::lock_guard<std::mutex> lk(_wsHelper->_subThreadWsMessageQueueMutex);
                                               
                std::list<WsMessage*>::iterator iter = _wsHelper->_subThreadWsMessageQueue->begin();
                
                int bytesWrite = 0;
                for (; iter != _wsHelper->_subThreadWsMessageQueue->end();)
                {
                    WsMessage* subThreadMsg = *iter;
                    
                    if ( WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what
                      || WS_MSG_TO_SUBTRHEAD_SENDING_BINARY == subThreadMsg->what)
                    {
                        Data* data = (Data*)subThreadMsg->obj;

                        const size_t c_bufferSize = WS_WRITE_BUFFER_SIZE;

                        size_t remaining = data->len - data->issued;
                        size_t n = min(remaining, c_bufferSize );
                        CCLOG("[websocket:send] total: %d, sent: %d, remaining: %d, buffer size: %d", static_cast<int>(data->len), static_cast<int>(data->issued), static_cast<int>(remaining), static_cast<int>(n));

                        unsigned char* buf = new unsigned char[LWS_SEND_BUFFER_PRE_PADDING + n + LWS_SEND_BUFFER_POST_PADDING];

                        memcpy((char*)&buf[LWS_SEND_BUFFER_PRE_PADDING], data->bytes + data->issued, n);
                        
                        int writeProtocol;
                        
                        if (data->issued == 0) {
							if (WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what)
							{
								writeProtocol = LWS_WRITE_TEXT;
							}
							else
							{
								writeProtocol = LWS_WRITE_BINARY;
							}

							// If we have more than 1 fragment
							if (data->len > c_bufferSize)
								writeProtocol |= LWS_WRITE_NO_FIN;
                        } else {
                        	// we are in the middle of fragments
                        	writeProtocol = LWS_WRITE_CONTINUATION;
                        	// and if not in the last fragment
                        	if (remaining != n)
                        		writeProtocol |= LWS_WRITE_NO_FIN;
                        }

                        bytesWrite = libwebsocket_write(wsi,  &buf[LWS_SEND_BUFFER_PRE_PADDING], n, (libwebsocket_write_protocol)writeProtocol);
                        CCLOG("[websocket:send] bytesWrite => %d", bytesWrite);

                        // Buffer overrun?
                        if (bytesWrite < 0)
                        {
                            break;
                        }
                        // Do we have another fragments to send?
                        else if (remaining != n)
                        {
                            data->issued += n;
                            break;
                        }
                        // Safely done!
                        else
                        {
                            CC_SAFE_DELETE_ARRAY(data->bytes);
                            CC_SAFE_DELETE(data);
                            CC_SAFE_DELETE_ARRAY(buf);
                            _wsHelper->_subThreadWsMessageQueue->erase(iter++);
                            CC_SAFE_DELETE(subThreadMsg);
                        }
                    }
                }
                
                /* get notified as soon as we can write again */
                
                libwebsocket_callback_on_writable(ctx, wsi);
            }
            break;
            
        case LWS_CALLBACK_CLOSED:
            {
                
                CCLOG("%s", "connection closing..");

                _wsHelper->quitSubThread();
                
                if (_readyState != State::kStateClosed)
                {
                    WsMessage* msg = new WsMessage();
                    _readyState = State::kStateClosed;
                    msg->what = WS_MSG_TO_UITHREAD_CLOSE;
                    _wsHelper->sendMessageToUIThread(msg);
                }
            }
            break;
            
        case LWS_CALLBACK_CLIENT_RECEIVE:
            {
                if (in && len > 0)
                {
                    // Accumulate the data (increasing the buffer as we go)
                    if (_currentDataLen == 0)
                    {
                        _currentData = new char[len];
                        memcpy (_currentData, in, len);
                        _currentDataLen = len;
                    }
                    else
                    {
                        char *new_data = new char [_currentDataLen + len];
                        memcpy (new_data, _currentData, _currentDataLen);
                        memcpy (new_data + _currentDataLen, in, len);
                        CC_SAFE_DELETE_ARRAY(_currentData);
                        _currentData = new_data;
                        _currentDataLen = _currentDataLen + len;
                    }

                    _pendingFrameDataLen = libwebsockets_remaining_packet_payload (wsi);

                    if (_pendingFrameDataLen > 0)
                    {
                        //CCLOG("%ld bytes of pending data to receive, consider increasing the libwebsocket rx_buffer_size value.", _pendingFrameDataLen);
                    }
                    
                    // If no more data pending, send it to the client thread
                    if (_pendingFrameDataLen == 0)
                    {
						WsMessage* msg = new WsMessage();
						msg->what = WS_MSG_TO_UITHREAD_MESSAGE;

						char* bytes = nullptr;
						Data* data = new Data();

						if (lws_frame_is_binary(wsi))
						{

							bytes = new char[_currentDataLen];
							data->isBinary = true;
						}
						else
						{
							bytes = new char[_currentDataLen+1];
							bytes[_currentDataLen] = '\0';
							data->isBinary = false;
						}

						memcpy(bytes, _currentData, _currentDataLen);

						data->bytes = bytes;
						data->len = _currentDataLen;
						msg->obj = (void*)data;

						CC_SAFE_DELETE_ARRAY(_currentData);
						_currentData = nullptr;
						_currentDataLen = 0;

						_wsHelper->sendMessageToUIThread(msg);
                    }
                }
            }
            break;
        default:
            break;
        
	}
    
	return 0;
}