コード例 #1
0
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
}
コード例 #2
0
    //--------------------------------------------------------------
    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;
    }