static int unreal_networking_client( struct lws *Wsi, enum lws_callback_reasons Reason, void *User, void *In, size_t Len) { struct lws_context *Context = lws_get_context(Wsi); FWebSocket* Socket = (FWebSocket*)lws_context_user(Context); switch (Reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: { Socket->ConnectedCallBack.Broadcast(); lws_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); check(Socket->Wsi == Wsi); } break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: { Socket->ErrorCallBack.Broadcast(); return -1; } break; case LWS_CALLBACK_CLIENT_RECEIVE: { // push it on the socket. Socket->OnRawRecieve(In, (uint32)Len, !!lws_frame_is_binary(Wsi)); check(Socket->Wsi == Wsi); lws_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); break; } case LWS_CALLBACK_CLIENT_WRITEABLE: { check(Socket->Wsi == Wsi); Socket->OnRawWebSocketWritable(Wsi); lws_callback_on_writable(Wsi); lws_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); break; } case LWS_CALLBACK_CLOSED: { Socket->ErrorCallBack.Broadcast(); return -1; } } return 0; }
int bmx_libwebsockets_lws_frame_is_binary(struct libwebsocket * wsi) { return lws_frame_is_binary(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; }
int WebSocket::onSocketCallback(struct libwebsocket_context *ctx, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { //CCLOG("socket callback for %d reason", reason); CCAssert(_wsContext == NULL || ctx == _wsContext, "Invalid context."); CCAssert(_wsInstance == NULL || wsi == NULL || 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 = NULL; if (reason == LWS_CALLBACK_CLIENT_CONNECTION_ERROR || (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == kStateConnecting) || (reason == LWS_CALLBACK_DEL_POLL_FD && _readyState == kStateConnecting) ) { msg = new WsMessage(); msg->what = WS_MSG_TO_UITHREAD_ERROR; _readyState = kStateClosing; } else if (reason == LWS_CALLBACK_PROTOCOL_DESTROY && _readyState == 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 = 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: { pthread_mutex_lock(&_wsHelper->_subThreadWsMessageQueueMutex); std::list<WsMessage*>::iterator iter = _wsHelper->_subThreadWsMessageQueue->begin(); int bytesWrite = 0; for (; iter != _wsHelper->_subThreadWsMessageQueue->end(); ++iter) { 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; unsigned char* buf = new unsigned char[LWS_SEND_BUFFER_PRE_PADDING + data->len + LWS_SEND_BUFFER_POST_PADDING]; memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, data->len); memcpy((char*)&buf[LWS_SEND_BUFFER_PRE_PADDING], data->bytes, data->len); enum libwebsocket_write_protocol writeProtocol; if (WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what) { writeProtocol = LWS_WRITE_TEXT; } else { writeProtocol = LWS_WRITE_BINARY; } bytesWrite = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], data->len, writeProtocol); if (bytesWrite < 0) { CCLOGERROR("%s", "libwebsocket_write error..."); } if (bytesWrite < data->len) { CCLOGERROR("Partial write LWS_CALLBACK_CLIENT_WRITEABLE\n"); } CC_SAFE_DELETE_ARRAY(data->bytes); CC_SAFE_DELETE(data); CC_SAFE_DELETE_ARRAY(buf); } CC_SAFE_DELETE(subThreadMsg); } _wsHelper->_subThreadWsMessageQueue->clear(); pthread_mutex_unlock(&_wsHelper->_subThreadWsMessageQueueMutex); /* 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 != kStateClosed) { WsMessage* msg = new WsMessage(); _readyState = kStateClosed; msg->what = WS_MSG_TO_UITHREAD_CLOSE; _wsHelper->sendMessageToUIThread(msg); } } break; case LWS_CALLBACK_CLIENT_RECEIVE: { if (in && len > 0) { WsMessage* msg = new WsMessage(); msg->what = WS_MSG_TO_UITHREAD_MESSAGE; char* bytes = NULL; Data* data = new Data(); if (lws_frame_is_binary(wsi)) { bytes = new char[len]; data->isBinary = true; } else { bytes = new char[len+1]; bytes[len] = '\0'; data->isBinary = false; } memcpy(bytes, in, len); data->bytes = bytes; data->len = len; msg->obj = (void*)data; _wsHelper->sendMessageToUIThread(msg); } } break; default: break; } return 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; }