diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp index 64d5e4c7ef78108eca57dea52f725780311a6a17..b9c6cc032ca6cc254d7d94bed84a78e361c08a36 100644 --- a/src/websockets/qwebsocket.cpp +++ b/src/websockets/qwebsocket.cpp @@ -402,7 +402,10 @@ qint64 QWebSocket::sendBinaryMessage(const QByteArray &data) Any data in the write buffer is flushed before the socket is closed. The \a closeCode is a QWebSocketProtocol::CloseCode indicating the reason to close, and - \a reason describes the reason of the closure more in detail + \a reason describes the reason of the closure more in detail. All control + frames, including the Close frame, are limited to 125 bytes. Since two of + these are used for \a closeCode the maximum length of \a reason is 123! If + \a reason exceeds this limit it will be truncated. */ void QWebSocket::close(QWebSocketProtocol::CloseCode closeCode, const QString &reason) { diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index 45d6a6f40135f1d59852cb7dd55eb89672afd5bc..9b27ad21a130ca917501ddaedd6906413e686b07 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -334,12 +334,14 @@ void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString r if (!m_isClosingHandshakeSent) { Q_Q(QWebSocket); m_closeCode = closeCode; - m_closeReason = reason; + // 125 is the maximum length of a control frame, and 2 bytes are used for the close code: + const QByteArray reasonUtf8 = reason.toUtf8().left(123); + m_closeReason = QString::fromUtf8(reasonUtf8); const quint16 code = qToBigEndian<quint16>(closeCode); QByteArray payload; payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2); - if (!reason.isEmpty()) - payload.append(reason.toUtf8()); + if (!reasonUtf8.isEmpty()) + payload.append(reasonUtf8); quint32 maskingKey = 0; if (m_mustMask) { maskingKey = generateMaskingKey(); @@ -347,6 +349,8 @@ void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString r } QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose, payload.size(), maskingKey, true); + + Q_ASSERT(payload.length() <= 125); frame.append(payload); m_pSocket->write(frame); m_pSocket->flush(); diff --git a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp index e80eeb100114667c7522c54853f0b0ed3a26280b..4dc60c74d10f5d170319418f80bb6f98633dee1e 100644 --- a/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp +++ b/tests/auto/websockets/qwebsocket/tst_qwebsocket.cpp @@ -143,6 +143,7 @@ private Q_SLOTS: #ifndef QT_NO_NETWORKPROXY void tst_setProxy(); #endif + void overlongCloseReason(); }; tst_QWebSocket::tst_QWebSocket() @@ -732,6 +733,34 @@ void tst_QWebSocket::tst_setProxy() socket.setProxy(proxy); QCOMPARE(socket.proxy(), proxy); } + +void tst_QWebSocket::overlongCloseReason() +{ + EchoServer echoServer; + + QWebSocket socket; + + //should return 0 because socket is not open yet + QCOMPARE(socket.sendTextMessage(QStringLiteral("1234")), 0); + + QSignalSpy socketConnectedSpy(&socket, SIGNAL(connected())); + QSignalSpy socketDisconnectedSpy(&socket, SIGNAL(disconnected())); + QSignalSpy serverConnectedSpy(&echoServer, SIGNAL(newConnection(QUrl))); + + QUrl url = QUrl(QStringLiteral("ws://") + echoServer.hostAddress().toString() + + QStringLiteral(":") + QString::number(echoServer.port())); + socket.open(url); + QTRY_COMPARE(socketConnectedSpy.count(), 1); + QTRY_COMPARE(serverConnectedSpy.count(), 1); + + const QString reason(200, QChar::fromLatin1('a')); + socket.close(QWebSocketProtocol::CloseCodeGoingAway, reason); + QCOMPARE(socket.closeCode(), QWebSocketProtocol::CloseCodeGoingAway); + // Max length of a control frame is 125, but 2 bytes are used for the close code: + QCOMPARE(socket.closeReason().length(), 123); + QCOMPARE(socket.closeReason(), reason.leftRef(123)); + QTRY_COMPARE(socketDisconnectedSpy.count(), 1); +} #endif // QT_NO_NETWORKPROXY QTEST_MAIN(tst_QWebSocket)