void receiveRequests() { // service ready klog("spawner server ready"); // defining size for messages const size_t requestLenMax = sizeof(MessageHeader) + sizeof(SpawnCommandSpawnRequest) + 1024; while (true) { // creating buffer for message uint8_t requestBuffer[requestLenMax]; // receive incoming request MessageReceiveStatus stat = ReceiveMessage(requestBuffer, requestLenMax); if (stat != MESSAGE_RECEIVE_STATUS_SUCCESSFUL) protocolError("receiving command failed with code %i", stat); MessageHeader *header = (MessageHeader*) requestBuffer; SpawnCommandHeader *commandHeader = (SpawnCommandHeader*) MESSAGE_CONTENT(header); if (commandHeader->command == SPAWN_COMMAND_SPAWN_REQUEST) processSpawnRequest((SpawnCommandSpawnRequest*) commandHeader, header->sender, header->transaction); else if (commandHeader->command == SPAWN_COMMAND_SHUTDOWN_MACHINE || commandHeader->command == SPAWN_COMMAND_REBOOT_MACHINE) processHealtMachine(commandHeader->command); else protocolError("received unknown command: code %i, task %i", commandHeader->command, header->sender); } }
void Protocol::handleSocketError(){ switch (error()){ case QAbstractSocket::RemoteHostClosedError: emit closed(); break; case QAbstractSocket::HostNotFoundError: protocolError(tr("The host was not found. Please check the " "host name and port settings.")); break; case QAbstractSocket::ConnectionRefusedError: protocolError(tr("The connection was refused by the server. " "Check that the host name and port " "settings are correct." )); break; default: emit protocolError(errorString()); } }
void ExistenceChecker::onError(const std::string &address, Kullo::Api::NetworkError error) { setLocked(false); switch (error) { case Kullo::Api::NetworkError::Server: Log.e() << "Server error."; emit serverError(QString::fromStdString(address)); break; case Kullo::Api::NetworkError::Protocol: Log.e() << "Protocol error."; emit protocolError(QString::fromStdString(address)); break; case Kullo::Api::NetworkError::Connection: Log.e() << "Connection error."; emit networkError(QString::fromStdString(address)); break; default: kulloAssert(false); } }
void EnginioBackendConnection::onSocketReadyRead() { // WebSocket Protocol (RFC6455) // Base Framing Protocol // http://tools.ietf.org/html/rfc6455#section-5.2 // // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-------+-+-------------+-------------------------------+ // |F|R|R|R| opcode|M| Payload len | Extended payload length | // |I|S|S|S| (4) |A| (7) | (16/64) | // |N|V|V|V| |S| | (if payload len==126/127) | // | |1|2|3| |K| | | // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + // | Extended payload length continued, if payload len == 127 | // + - - - - - - - - - - - - - - - +-------------------------------+ // | |Masking-key, if MASK set to 1 | // +-------------------------------+-------------------------------+ // | Masking-key (continued) | Payload Data | // +-------------------------------- - - - - - - - - - - - - - - - + // : Payload Data continued ... : // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // | Payload Data continued ... | // +---------------------------------------------------------------+ while (_tcpSocket->bytesAvailable()) { switch (_protocolDecodeState) { case HandshakePending: { // The response is closed by a CRLF line on its own (e.g. ends with two newlines). while (_handshakeReply.isEmpty() || (!_handshakeReply.endsWith(QString(CRLF % CRLF).toUtf8()) // According to documentation QIODevice::readLine replaces newline characters on // Windows with '\n', so just to be on the safe side: && !_handshakeReply.endsWith(QByteArrayLiteral("\n\n")))) { if (!_tcpSocket->bytesAvailable()) return; _handshakeReply.append(_tcpSocket->readLine()); } QString response = QString::fromUtf8(_handshakeReply); _handshakeReply.clear(); int statusCode = extractResponseStatus(response); QString secWebSocketAccept = extractResponseHeader(SecWebSocketAcceptHeader, response, /* ignoreCase */ false); bool hasValidKey = secWebSocketAccept == gBase64EncodedSha1VerificationKey; if (statusCode != 101 || !hasValidKey || extractResponseHeader(UpgradeHeader, response) != QStringLiteral("websocket") || extractResponseHeader(ConnectionHeader, response) != QStringLiteral("upgrade") ) return protocolError("Handshake failed!"); _keepAliveTimer.start(TwoMinutes, this); _protocolDecodeState = FrameHeaderPending; emit stateChanged(ConnectedState); } // Fall-through. case FrameHeaderPending: { if (quint64(_tcpSocket->bytesAvailable()) < DefaultHeaderLength) return; // Large payload. if (_payloadLength == LargePayloadMarker) { if (quint64(_tcpSocket->bytesAvailable()) < LargePayloadHeaderLength) return; char data[LargePayloadHeaderLength]; if (quint64(_tcpSocket->read(data, LargePayloadHeaderLength)) != LargePayloadHeaderLength) return protocolError("Reading large payload length failed!"); if (data[0] & MSB) return protocolError("The most significant bit of a large payload length must be 0!", MessageTooBigCloseStatus); // 8 bytes interpreted as a 64-bit unsigned integer _payloadLength = qFromBigEndian<quint64>(reinterpret_cast<uchar*>(data)); _protocolDecodeState = PayloadDataPending; break; } char data[DefaultHeaderLength]; if (quint64(_tcpSocket->read(data, DefaultHeaderLength)) != DefaultHeaderLength) return protocolError("Reading header failed!"); if (!_payloadLength) { // This is the initial frame header data. _isFinalFragment = (data[0] & FIN); _protocolOpcode = static_cast<WebSocketOpcode>(data[0] & OPC); _isPayloadMasked = (data[1] & MSK); _payloadLength = (data[1] & LEN); if (_isPayloadMasked) return protocolError("Invalid masked frame received from server."); // For data length 0-125 LEN is the payload length. if (_payloadLength < NormalPayloadMarker) _protocolDecodeState = PayloadDataPending; } else { Q_ASSERT(_payloadLength == NormalPayloadMarker); // Normal sized payload: 2 bytes interpreted as the payload // length expressed in network byte order (e.g. big endian). _payloadLength = qFromBigEndian<quint16>(reinterpret_cast<uchar*>(data)); _protocolDecodeState = PayloadDataPending; } break; } case PayloadDataPending: { if (static_cast<quint64>(_tcpSocket->bytesAvailable()) < _payloadLength) return; if (_protocolOpcode == ConnectionCloseOp) { WebSocketCloseStatus closeStatus = UnknownCloseStatus; if (_payloadLength >= DefaultHeaderLength) { char data[DefaultHeaderLength]; if (quint64(_tcpSocket->read(data, DefaultHeaderLength)) != DefaultHeaderLength) return protocolError("Reading connection close status failed!"); closeStatus = static_cast<WebSocketCloseStatus>(qFromBigEndian<quint16>(reinterpret_cast<uchar*>(data))); // The body may contain UTF-8-encoded data with value /reason/, // the interpretation of this data is however not defined by the // specification. Further more the data is not guaranteed to be // human readable, thus it is safe for us to just discard the rest // of the message at this point. } qDebug() << "Connection closed by the server with status:" << closeStatus; QJsonObject data; data[EnginioString::messageType] = QStringLiteral("close"); data[EnginioString::status] = closeStatus; emit dataReceived(data); close(closeStatus); _tcpSocket->close(); return; } // We received data from the server so restart the timer. _keepAliveTimer.start(TwoMinutes, this); _applicationData.append(_tcpSocket->read(_payloadLength)); _protocolDecodeState = FrameHeaderPending; _payloadLength = 0; if (!_isFinalFragment) break; switch (_protocolOpcode) { case TextFrameOp: { QJsonObject data = QJsonDocument::fromJson(_applicationData).object(); data[EnginioString::messageType] = QStringLiteral("data"); emit dataReceived(data); break; } case PingOp:{ // We must send back identical application data as found in the message. QByteArray payload = _applicationData; QByteArray maskingKey = generateMaskingKey(); QByteArray message = constructFrameHeader(/*isFinalFragment*/ true, PongOp, payload.size(), maskingKey); Q_ASSERT(!message.isEmpty()); maskData(payload, maskingKey); message.append(payload); _tcpSocket->write(message); break; } case PongOp: _pingTimeoutTimer.stop(); emit pong(); break; default: protocolError("WebSocketOpcode not yet supported.", UnsupportedDataTypeCloseStatus); qWarning() << "\t\t->" << _protocolOpcode; } _applicationData.clear(); break; } } } }
void EnginioBackendConnection::onSocketConnectionError(QAbstractSocket::SocketError error) { protocolError("Socket connection error."); qWarning() << "\t\t->" << error; }
void Protocol::prepareStateMachine(){ qDebug() << "Protocol: Initialising Protocol State"; QStateMachine *machine = new QStateMachine(this); /** * Main states are connected, disconnected and done, we start disconnected * and turn to connected when the socket is operational. * All the other states are part of the connected state */ QState *disconnected = new QState(); QState *connected = new QState(); QFinalState *done = new QFinalState(); /** * When first connected, we need to know the protocol version, * then request authentication. We then either turn to the authenticated * state or flee to *done */ QState *waitingproto = new QState(connected); QState *waitingauthrequest = new QState(connected); QState *waitingauthstatus = new QState(connected); QState *authenticated = new QState(connected); connected->setInitialState(waitingproto); /** * When authenticated, the user must provide some information about himself * (nickname, status, planet picture, user picture, attack picture) * Then the user can request to join the chat room, get the list of games and users * join a game or create a game * In the chat room, the user sends and receives messages, and can eventually exit */ QState *waitinguserdetails = new QState(authenticated); QState *waitingcommand = new QState(authenticated); QState *inchat = new QState(authenticated); QState *waitinggamecreation = new QState(authenticated); QState *waitinggamelist = new QState(authenticated); QState *waitinguserlist = new QState(authenticated); QState *joinedgame = new QState(authenticated); authenticated->setInitialState(waitinguserdetails); /** * When entering the game, we wait for the users to show up * then receive the game map * we then wait for the game to unpause, and the game progresses till * there's a winner or cancellation */ QState *waitinguser = new QState(joinedgame); QState *waitingmap = new QState(joinedgame); QState *paused = new QState(joinedgame); QState *ingame = new QState(joinedgame); joinedgame->setInitialState(waitinguser); qDebug() << "Protocol: Connecting Protocol Signals"; disconnected->addTransition(this, SIGNAL(connected()), connected); connected->addTransition(this, SIGNAL(closed()), done); waitingproto->addTransition(this, SIGNAL(protocol(QString)), waitingauthrequest); waitingauthrequest->addTransition(this, SIGNAL(authrequest(QString,QString)), waitingauthstatus); waitingauthstatus->addTransition(this, SIGNAL(authenticated()), authenticated); waitingauthstatus->addTransition(this, SIGNAL(protocolError(QString)), done); machine->addState(disconnected); machine->addState(connected); machine->setInitialState(disconnected); qDebug() << "Protocol: Starting State Machine"; machine->start(); }