diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-03-23 16:00:29 +0100 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2020-03-23 16:00:29 +0100 |
commit | 6b19edaefe7edcdc12a1faa269d77e42a14fc81d (patch) | |
tree | 2c5a803fc8f7b0e8b2c83f49dcd846c2dba72e16 /src | |
parent | 5fc2b148cfa763007810d7b6256ea50d82bf7e45 (diff) | |
parent | 8fc139318b2f870aa633e93874b3fa6e9d1113ae (diff) | |
download | qtwebsockets-6b19edaefe7edcdc12a1faa269d77e42a14fc81d.tar.gz |
Merge remote-tracking branch 'origin/dev' into wip/cmake
Conflicts:
dependencies.yaml
Change-Id: I1608720522e974c7107f00599a9fb70f17865189
Diffstat (limited to 'src')
-rw-r--r-- | src/imports/qmlwebsockets/plugins.qmltypes | 2 | ||||
-rw-r--r-- | src/websockets/doc/src/external-resources.qdoc | 5 | ||||
-rw-r--r-- | src/websockets/qsslserver.cpp | 6 | ||||
-rw-r--r-- | src/websockets/qsslserver_p.h | 3 | ||||
-rw-r--r-- | src/websockets/qwebsocket.cpp | 77 | ||||
-rw-r--r-- | src/websockets/qwebsocket.h | 6 | ||||
-rw-r--r-- | src/websockets/qwebsocket_p.cpp | 41 | ||||
-rw-r--r-- | src/websockets/qwebsocket_p.h | 5 | ||||
-rw-r--r-- | src/websockets/qwebsocketframe.cpp | 122 | ||||
-rw-r--r-- | src/websockets/qwebsocketframe_p.h | 40 | ||||
-rw-r--r-- | src/websockets/qwebsockethandshakerequest.cpp | 15 | ||||
-rw-r--r-- | src/websockets/qwebsockethandshakerequest_p.h | 2 | ||||
-rw-r--r-- | src/websockets/qwebsocketserver.cpp | 25 | ||||
-rw-r--r-- | src/websockets/qwebsocketserver.h | 3 | ||||
-rw-r--r-- | src/websockets/qwebsocketserver_p.cpp | 56 |
15 files changed, 200 insertions, 208 deletions
diff --git a/src/imports/qmlwebsockets/plugins.qmltypes b/src/imports/qmlwebsockets/plugins.qmltypes index 79e2f97..cabe5a2 100644 --- a/src/imports/qmlwebsockets/plugins.qmltypes +++ b/src/imports/qmlwebsockets/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtWebSockets 1.13' +// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtWebSockets 1.15' Module { dependencies: [] diff --git a/src/websockets/doc/src/external-resources.qdoc b/src/websockets/doc/src/external-resources.qdoc index 40e4257..ce39b9b 100644 --- a/src/websockets/doc/src/external-resources.qdoc +++ b/src/websockets/doc/src/external-resources.qdoc @@ -86,12 +86,12 @@ */ /*! - \externalpage http://autobahn.ws/testsuite/ + \externalpage https://github.com/crossbario/autobahn-testsuite \title Autobahn|Testsuite */ /*! - \externalpage http://autobahn.ws/testsuite/installation.html + \externalpage https://github.com/crossbario/autobahn-testsuite/blob/master/README.md#installation \title Autobahn|Testsuite installation documentation */ @@ -103,6 +103,7 @@ /*! \externalpage https://bugzilla.mozilla.org/show_bug.cgi?id=594502 \title Firefox bug 594502 +*/ /* This prevents autolinking of each occurrence of 'WebSocket' diff --git a/src/websockets/qsslserver.cpp b/src/websockets/qsslserver.cpp index ec645c9..7f33a9d 100644 --- a/src/websockets/qsslserver.cpp +++ b/src/websockets/qsslserver.cpp @@ -121,6 +121,12 @@ void QSslServer::incomingConnection(qintptr socket) this, &QSslServer::socketEncrypted); connect(pSslSocket, &QSslSocket::preSharedKeyAuthenticationRequired, this, &QSslServer::preSharedKeyAuthenticationRequired); + connect(pSslSocket, &QSslSocket::alertSent, + this, &QSslServer::alertSent); + connect(pSslSocket, &QSslSocket::alertReceived, + this, &QSslServer::alertReceived); + connect(pSslSocket, &QSslSocket::handshakeInterruptedOnError, + this, &QSslServer::handshakeInterruptedOnError); Q_EMIT startedEncryptionHandshake(pSslSocket); diff --git a/src/websockets/qsslserver_p.h b/src/websockets/qsslserver_p.h index 6283058..e0c77fa 100644 --- a/src/websockets/qsslserver_p.h +++ b/src/websockets/qsslserver_p.h @@ -78,6 +78,9 @@ Q_SIGNALS: void peerVerifyError(const QSslError &error); void newEncryptedConnection(); void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator); + void alertSent(QAlertLevel level, QAlertType type, const QString &description); + void alertReceived(QAlertLevel level, QAlertType type, const QString &description); + void handshakeInterruptedOnError(const QSslError &error); void startedEncryptionHandshake(QSslSocket *socket); protected: diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp index ade1eb4..e9bba37 100644 --- a/src/websockets/qwebsocket.cpp +++ b/src/websockets/qwebsocket.cpp @@ -261,6 +261,67 @@ not been filled in with new information when the signal returns. \sa QSslSocket::preSharedKeyAuthenticationRequired() */ /*! + \fn void QWebSocket::peerVerifyError(const QSslError &error) + \since 6.0 + + QWebSocket can emit this signal several times during the SSL handshake, + before encryption has been established, to indicate that an error has + occurred while establishing the identity of the peer. The \a error is + usually an indication that QWebSocket is unable to securely identify the + peer. + + This signal provides you with an early indication when something's wrong. + By connecting to this signal, you can manually choose to tear down the + connection from inside the connected slot before the handshake has + completed. If no action is taken, QWebSocket will proceed to emitting + QWebSocket::sslErrors(). + + \sa sslErrors() +*/ +/*! + \fn void QWebSocket::alertSent(QAlertLevel level, QAlertType type, const QString &description) + \since 6.0 + + QWebSocket emits this signal if an alert message was sent to a peer. \a level + describes if it was a warning or a fatal error. \a type gives the code + of the alert message. When a textual description of the alert message is + available, it is supplied in \a description. + + \note This signal is mostly informational and can be used for debugging + purposes, normally it does not require any actions from the application. + \note Not all backends support this functionality. + + \sa alertReceived(), QSslSocket::QAlertLevel, QSslSocket::QAlertType +*/ +/*! + \fn void QWebSocket::alertReceived(QAlertLevel level, QAlertType type, const QString &description) + \since 6.0 + + QWebSocket emits this signal if an alert message was received from a peer. + \a level tells if the alert was fatal or it was a warning. \a type is the + code explaining why the alert was sent. When a textual description of + the alert message is available, it is supplied in \a description. + + \note The signal is mostly for informational and debugging purposes and does not + require any handling in the application. If the alert was fatal, underlying + backend will handle it and close the connection. + \note Not all backends support this functionality. + + \sa alertSent(), QSslSocket::QAlertLevel, QSslSocket::QAlertType +*/ +/*! + \fn void QWebSocket::handshakeInterruptedOnError(const QSslError &error) + \since 6.0 + + QWebSocket emits this signal if a certificate verification error was + found and if early error reporting was enabled in QSslConfiguration. + An application is expected to inspect the \a error and decide if + it wants to continue the handshake, or abort it and send an alert message + to the peer. The signal-slot connection must be direct. + + \sa continueInterruptedHandshake(), sslErrors(), QSslConfiguration::setHandshakeMustInterruptOnError() +*/ +/*! \fn void QWebSocket::pong(quint64 elapsedTime, const QByteArray &payload) Emitted when a pong message is received in reply to a previous ping. @@ -483,6 +544,22 @@ void QWebSocket::ignoreSslErrors() } /*! + \since 6.0 + + If an application wants to conclude a handshake even after receiving + handshakeInterruptedOnError() signal, it must call this function. + This call must be done from a slot function attached to the signal. + The signal-slot connection must be direct. + + \sa handshakeInterruptedOnError(), QSslConfiguration::setHandshakeMustInterruptOnError() +*/ +void QWebSocket::continueInterruptedHandshake() +{ + Q_D(QWebSocket); + d->continueInterruptedHandshake(); +} + +/*! \overload This method tells QWebSocket to ignore the errors given in \a errors. diff --git a/src/websockets/qwebsocket.h b/src/websockets/qwebsocket.h index 4944689..530127f 100644 --- a/src/websockets/qwebsocket.h +++ b/src/websockets/qwebsocket.h @@ -109,6 +109,8 @@ public: #ifndef QT_NO_SSL void ignoreSslErrors(const QList<QSslError> &errors); + void continueInterruptedHandshake(); + void setSslConfiguration(const QSslConfiguration &sslConfiguration); QSslConfiguration sslConfiguration() const; #endif @@ -143,8 +145,12 @@ Q_SIGNALS: void bytesWritten(qint64 bytes); #ifndef QT_NO_SSL + void peerVerifyError(const QSslError &error); void sslErrors(const QList<QSslError> &errors); void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator); + void alertSent(QAlertLevel level, QAlertType type, const QString &description); + void alertReceived(QAlertLevel level, QAlertType type, const QString &description); + void handshakeInterruptedOnError(const QSslError &error); #endif private: diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index 36aefd9..6ca8d1a 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -285,6 +285,18 @@ void QWebSocketPrivate::ignoreSslErrors() } /*! + * \internal + */ +void QWebSocketPrivate::continueInterruptedHandshake() +{ + if (Q_LIKELY(m_pSocket)) { + QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket); + if (Q_LIKELY(pSslSocket)) + pSslSocket->continueInterruptedHandshake(); + } +} + +/*! * \internal */ void QWebSocketPrivate::_q_updateSslConfiguration() @@ -569,7 +581,7 @@ void QWebSocketPrivate::enableMasking(bool enable) /*! * \internal */ -void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket) +void QWebSocketPrivate::makeConnections(QTcpSocket *pTcpSocket) { Q_ASSERT(pTcpSocket); Q_Q(QWebSocket); @@ -612,6 +624,14 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket) q, &QWebSocket::sslErrors); QObjectPrivate::connect(sslSocket, &QSslSocket::encrypted, this, &QWebSocketPrivate::_q_updateSslConfiguration); + QObject::connect(sslSocket, &QSslSocket::peerVerifyError, + q, &QWebSocket::peerVerifyError); + QObject::connect(sslSocket, &QSslSocket::alertSent, + q, &QWebSocket::alertSent); + QObject::connect(sslSocket, &QSslSocket::alertReceived, + q, &QWebSocket::alertReceived); + QObject::connect(sslSocket, &QSslSocket::handshakeInterruptedOnError, + q, &QWebSocket::handshakeInterruptedOnError); } else #endif // QT_NO_SSL { @@ -636,6 +656,10 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket) &QWebSocketPrivate::processPong); QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this, &QWebSocketPrivate::processClose); + + //fire readyread, in case we already have data inside the tcpSocket + if (pTcpSocket->bytesAvailable()) + Q_EMIT pTcpSocket->readyRead(); } /*! @@ -1000,8 +1024,8 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) errorDescription = QWebSocket::tr("Malformed header in response: %1.").arg(headerLine); break; } - lastHeader = m_headers.insertMulti(headerLine.left(colonPos).trimmed().toLower(), - headerLine.mid(colonPos + 1).trimmed()); + lastHeader = m_headers.insert(headerLine.left(colonPos).trimmed().toLower(), + headerLine.mid(colonPos + 1).trimmed()); } } @@ -1048,8 +1072,7 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) } else if (m_httpStatusCode == 400) { //HTTP/1.1 400 Bad Request if (!version.isEmpty()) { - const QStringList versions = version.split(QStringLiteral(", "), - QString::SkipEmptyParts); + const QStringList versions = version.split(QStringLiteral(", "), Qt::SkipEmptyParts); if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) { //if needed to switch protocol version, then we are finished here //because we cannot handle other protocols than the RFC one (v13) @@ -1099,9 +1122,6 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS Q_ASSERT(m_pSocket); Q_Q(QWebSocket); QAbstractSocket::SocketState webSocketState = this->state(); - int port = 80; - if (m_request.url().scheme() == QStringLiteral("wss")) - port = 443; switch (socketState) { case QAbstractSocket::ConnectedState: @@ -1120,11 +1140,10 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS const auto format = QUrl::RemoveScheme | QUrl::RemoveUserInfo | QUrl::RemovePath | QUrl::RemoveQuery - | QUrl::RemoveFragment | QUrl::RemovePort; + | QUrl::RemoveFragment; const QString host = m_request.url().toString(format).mid(2); const QString handshake = createHandShakeRequest(m_resourceName, - host % QStringLiteral(":") - % QString::number(m_request.url().port(port)), + host, origin(), QString(), QString(), diff --git a/src/websockets/qwebsocket_p.h b/src/websockets/qwebsocket_p.h index e72daa5..d0fa38c 100644 --- a/src/websockets/qwebsocket_p.h +++ b/src/websockets/qwebsocket_p.h @@ -149,6 +149,7 @@ public: #ifndef QT_NO_SSL void ignoreSslErrors(const QList<QSslError> &errors); void ignoreSslErrors(); + void continueInterruptedHandshake(); void setSslConfiguration(const QSslConfiguration &sslConfiguration); QSslConfiguration sslConfiguration() const; void _q_updateSslConfiguration(); @@ -182,7 +183,7 @@ private: Q_REQUIRED_RESULT qint64 doWriteFrames(const QByteArray &data, bool isBinary); - void makeConnections(const QTcpSocket *pTcpSocket); + void makeConnections(QTcpSocket *pTcpSocket); void releaseConnections(const QTcpSocket *pTcpSocket); QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, @@ -248,7 +249,7 @@ private: int m_httpStatusCode; int m_httpMajorVersion, m_httpMinorVersion; QString m_httpStatusMessage; - QMap<QString, QString> m_headers; + QMultiMap<QString, QString> m_headers; friend class QWebSocketServerPrivate; #ifdef Q_OS_WASM diff --git a/src/websockets/qwebsocketframe.cpp b/src/websockets/qwebsocketframe.cpp index 11373a7..cfa63ed 100644 --- a/src/websockets/qwebsocketframe.cpp +++ b/src/websockets/qwebsocketframe.cpp @@ -64,128 +64,6 @@ QT_BEGIN_NAMESPACE /*! \internal */ -QWebSocketFrame::QWebSocketFrame() : - m_closeCode(QWebSocketProtocol::CloseCodeNormal), - m_closeReason(), - m_mask(0), - m_opCode(QWebSocketProtocol::OpCodeReservedC), - m_length(0), - m_payload(), - m_isFinalFrame(true), - m_rsv1(false), - m_rsv2(false), - m_rsv3(false), - m_isValid(false) -{ -} - -/*! - \internal - */ -QWebSocketFrame::QWebSocketFrame(const QWebSocketFrame &other) : - m_closeCode(other.m_closeCode), - m_closeReason(other.m_closeReason), - m_mask(other.m_mask), - m_opCode(other.m_opCode), - m_length(other.m_length), - m_payload(other.m_payload), - m_isFinalFrame(other.m_isFinalFrame), - m_rsv1(other.m_rsv1), - m_rsv2(other.m_rsv2), - m_rsv3(other.m_rsv3), - m_isValid(other.m_isValid), - m_processingState(other.m_processingState) -{ -} - -/*! - \internal - */ -QWebSocketFrame &QWebSocketFrame::operator =(const QWebSocketFrame &other) -{ - m_closeCode = other.m_closeCode; - m_closeReason = other.m_closeReason; - m_isFinalFrame = other.m_isFinalFrame; - m_mask = other.m_mask; - m_rsv1 = other.m_rsv1; - m_rsv2 = other.m_rsv2; - m_rsv3 = other.m_rsv3; - m_opCode = other.m_opCode; - m_length = other.m_length; - m_payload = other.m_payload; - m_isValid = other.m_isValid; - m_processingState = other.m_processingState; - - return *this; -} - -#ifdef Q_COMPILER_RVALUE_REFS -/*! - \internal - */ -QWebSocketFrame::QWebSocketFrame(QWebSocketFrame &&other) : - m_closeCode(qMove(other.m_closeCode)), - m_closeReason(qMove(other.m_closeReason)), - m_mask(qMove(other.m_mask)), - m_opCode(qMove(other.m_opCode)), - m_length(qMove(other.m_length)), - m_payload(qMove(other.m_payload)), - m_isFinalFrame(qMove(other.m_isFinalFrame)), - m_rsv1(qMove(other.m_rsv1)), - m_rsv2(qMove(other.m_rsv2)), - m_rsv3(qMove(other.m_rsv3)), - m_isValid(qMove(other.m_isValid)), - m_processingState(qMove(other.m_processingState)) -{} - - -/*! - \internal - */ -QWebSocketFrame &QWebSocketFrame::operator =(QWebSocketFrame &&other) -{ - qSwap(m_closeCode, other.m_closeCode); - qSwap(m_closeReason, other.m_closeReason); - qSwap(m_isFinalFrame, other.m_isFinalFrame); - qSwap(m_mask, other.m_mask); - qSwap(m_rsv1, other.m_rsv1); - qSwap(m_rsv2, other.m_rsv2); - qSwap(m_rsv3, other.m_rsv3); - qSwap(m_opCode, other.m_opCode); - qSwap(m_length, other.m_length); - qSwap(m_payload, other.m_payload); - qSwap(m_isValid, other.m_isValid); - qSwap(m_processingState, other.m_processingState); - - return *this; -} - -#endif - -/*! - \internal - */ -void QWebSocketFrame::swap(QWebSocketFrame &other) -{ - if (&other != this) { - qSwap(m_closeCode, other.m_closeCode); - qSwap(m_closeReason, other.m_closeReason); - qSwap(m_isFinalFrame, other.m_isFinalFrame); - qSwap(m_mask, other.m_mask); - qSwap(m_rsv1, other.m_rsv1); - qSwap(m_rsv2, other.m_rsv2); - qSwap(m_rsv3, other.m_rsv3); - qSwap(m_opCode, other.m_opCode); - qSwap(m_length, other.m_length); - qSwap(m_payload, other.m_payload); - qSwap(m_isValid, other.m_isValid); - qSwap(m_processingState, other.m_processingState); - } -} - -/*! - \internal - */ QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const { return isDone() ? m_closeCode : QWebSocketProtocol::CloseCodeGoingAway; diff --git a/src/websockets/qwebsocketframe_p.h b/src/websockets/qwebsocketframe_p.h index e2b4e9f..a8b9684 100644 --- a/src/websockets/qwebsocketframe_p.h +++ b/src/websockets/qwebsocketframe_p.h @@ -54,7 +54,7 @@ #include <QtCore/QString> #include <QtCore/QByteArray> #include <QtCore/QCoreApplication> -#include <limits.h> +#include <limits> #include "qwebsockets_global.h" #include "qwebsocketprotocol.h" @@ -64,25 +64,15 @@ QT_BEGIN_NAMESPACE class QIODevice; -const quint64 MAX_FRAME_SIZE_IN_BYTES = INT_MAX - 1; -const quint64 MAX_MESSAGE_SIZE_IN_BYTES = INT_MAX - 1; +const quint64 MAX_FRAME_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1; +const quint64 MAX_MESSAGE_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1; class Q_AUTOTEST_EXPORT QWebSocketFrame { Q_DECLARE_TR_FUNCTIONS(QWebSocketFrame) public: - QWebSocketFrame(); - QWebSocketFrame(const QWebSocketFrame &other); - - QWebSocketFrame &operator =(const QWebSocketFrame &other); - -#ifdef Q_COMPILER_RVALUE_REFS - QWebSocketFrame(QWebSocketFrame &&other); - QWebSocketFrame &operator =(QWebSocketFrame &&other); -#endif - - void swap(QWebSocketFrame &other); + QWebSocketFrame() = default; QWebSocketProtocol::CloseCode closeCode() const; QString closeReason() const; @@ -106,18 +96,12 @@ public: void readFrame(QIODevice *pIoDevice); private: - QWebSocketProtocol::CloseCode m_closeCode; QString m_closeReason; - quint32 m_mask; - QWebSocketProtocol::OpCode m_opCode; - quint64 m_length; QByteArray m_payload; - - bool m_isFinalFrame; - bool m_rsv1; - bool m_rsv2; - bool m_rsv3; - bool m_isValid; + quint64 m_length = 0; + quint32 m_mask = 0; + QWebSocketProtocol::CloseCode m_closeCode = QWebSocketProtocol::CloseCodeNormal; + QWebSocketProtocol::OpCode m_opCode = QWebSocketProtocol::OpCodeReservedC; enum ProcessingState { @@ -127,7 +111,13 @@ private: PS_READ_PAYLOAD, PS_DISPATCH_RESULT, PS_WAIT_FOR_MORE_DATA - } m_processingState{PS_READ_HEADER}; + } m_processingState = PS_READ_HEADER; + + bool m_isFinalFrame = true; + bool m_rsv1 = false; + bool m_rsv2 = false; + bool m_rsv3 = false; + bool m_isValid = false; ProcessingState readFrameHeader(QIODevice *pIoDevice); ProcessingState readFramePayloadLength(QIODevice *pIoDevice); diff --git a/src/websockets/qwebsockethandshakerequest.cpp b/src/websockets/qwebsockethandshakerequest.cpp index bfc8a3d..17275eb 100644 --- a/src/websockets/qwebsockethandshakerequest.cpp +++ b/src/websockets/qwebsockethandshakerequest.cpp @@ -230,7 +230,7 @@ void QWebSocketHandshakeRequest::readHandshake(QTextStream &textStream, int maxH clear(); return; } - const QStringList tokens = requestLine.split(' ', QString::SkipEmptyParts); + const QStringList tokens = requestLine.split(' ', Qt::SkipEmptyParts); if (Q_UNLIKELY(tokens.length() < 3)) { clear(); return; @@ -268,8 +268,8 @@ void QWebSocketHandshakeRequest::readHandshake(QTextStream &textStream, int maxH clear(); return; } - lastHeader = m_headers.insertMulti(headerLine.left(colonPos).trimmed().toLower(), - headerLine.mid(colonPos + 1).trimmed()); + lastHeader = m_headers.insert(headerLine.left(colonPos).trimmed().toLower(), + headerLine.mid(colonPos + 1).trimmed()); } if (m_headers.size() > maxHeaders) { clear(); @@ -301,7 +301,7 @@ void QWebSocketHandshakeRequest::readHandshake(QTextStream &textStream, int maxH const QStringList versionLines = m_headers.values(QStringLiteral("sec-websocket-version")); for (QStringList::const_iterator v = versionLines.begin(); v != versionLines.end(); ++v) { - const QStringList versions = (*v).split(QStringLiteral(","), QString::SkipEmptyParts); + const QStringList versions = (*v).split(QStringLiteral(","), Qt::SkipEmptyParts); for (QStringList::const_iterator i = versions.begin(); i != versions.end(); ++i) { bool ok = false; (void)(*i).toUInt(&ok); @@ -321,8 +321,7 @@ void QWebSocketHandshakeRequest::readHandshake(QTextStream &textStream, int maxH const QString upgrade = m_headers.value(QStringLiteral("upgrade"), QString()); //must be equal to "websocket", case-insensitive const QString connection = m_headers.value(QStringLiteral("connection"), QString()); - const QStringList connectionLine = connection.split(QStringLiteral(","), - QString::SkipEmptyParts); + const QStringList connectionLine = connection.split(QStringLiteral(","), Qt::SkipEmptyParts); QStringList connectionValues; for (QStringList::const_iterator c = connectionLine.begin(); c != connectionLine.end(); ++c) connectionValues << (*c).trimmed(); @@ -331,14 +330,14 @@ void QWebSocketHandshakeRequest::readHandshake(QTextStream &textStream, int maxH m_origin = m_headers.value(QStringLiteral("origin"), QString()); const QStringList protocolLines = m_headers.values(QStringLiteral("sec-websocket-protocol")); for (const QString& pl : protocolLines) { - const QStringList protocols = pl.split(QStringLiteral(","), QString::SkipEmptyParts); + const QStringList protocols = pl.split(QStringLiteral(","), Qt::SkipEmptyParts); for (const QString& p : protocols) m_protocols << p.trimmed(); } const QStringList extensionLines = m_headers.values(QStringLiteral("sec-websocket-extensions")); for (const QString& el : extensionLines) { - const QStringList extensions = el.split(QStringLiteral(","), QString::SkipEmptyParts); + const QStringList extensions = el.split(QStringLiteral(","), Qt::SkipEmptyParts); for (const QString& e : extensions) m_extensions << e.trimmed(); } diff --git a/src/websockets/qwebsockethandshakerequest_p.h b/src/websockets/qwebsockethandshakerequest_p.h index e5762df..e14c05d 100644 --- a/src/websockets/qwebsockethandshakerequest_p.h +++ b/src/websockets/qwebsockethandshakerequest_p.h @@ -91,7 +91,7 @@ private: int m_port; bool m_isSecure; bool m_isValid; - QMap<QString, QString> m_headers; + QMultiMap<QString, QString> m_headers; QList<QWebSocketProtocol::Version> m_versions; QString m_key; QString m_origin; diff --git a/src/websockets/qwebsocketserver.cpp b/src/websockets/qwebsocketserver.cpp index eafe3fd..b833503 100644 --- a/src/websockets/qwebsocketserver.cpp +++ b/src/websockets/qwebsocketserver.cpp @@ -656,31 +656,6 @@ qintptr QWebSocketServer::socketDescriptor() const return d->socketDescriptor(); } -/*! - \fn QWebSocketServer::nativeDescriptor - \deprecated - - Returns the native socket descriptor the server uses to listen for incoming instructions, - or -1 if the server is not listening. - If the server is using QNetworkProxy, the returned descriptor may not be usable with - native socket functions. - - \sa socketDescriptor(), setSocketDescriptor(), setNativeDescriptor(), isListening() - \since 5.12 - */ -/*! - \fn QWebSocketServer::setNativeDescriptor - \deprecated - - Sets the socket descriptor this server should use when listening for incoming connections to - \a socketDescriptor. - - Returns true if the socket is set successfully; otherwise returns false. - The socket is assumed to be in listening state. - - \sa socketDescriptor(), setSocketDescriptor(), nativeDescriptor(), isListening() - \since 5.12 - */ #else // ### Qt 6: Remove leftovers /*! \deprecated diff --git a/src/websockets/qwebsocketserver.h b/src/websockets/qwebsocketserver.h index ceb9106..478b35e 100644 --- a/src/websockets/qwebsocketserver.h +++ b/src/websockets/qwebsocketserver.h @@ -158,6 +158,9 @@ Q_SIGNALS: void peerVerifyError(const QSslError &error); void sslErrors(const QList<QSslError> &errors); void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator); + void alertSent(QAlertLevel level, QAlertType type, const QString &description); + void alertReceived(QAlertLevel level, QAlertType type, const QString &description); + void handshakeInterruptedOnError(const QSslError &error); #endif void closed(); }; diff --git a/src/websockets/qwebsocketserver_p.cpp b/src/websockets/qwebsocketserver_p.cpp index 8225b59..f922098 100644 --- a/src/websockets/qwebsocketserver_p.cpp +++ b/src/websockets/qwebsocketserver_p.cpp @@ -107,6 +107,12 @@ void QWebSocketServerPrivate::init() q, &QWebSocketServer::sslErrors); QObject::connect(pSslServer, &QSslServer::preSharedKeyAuthenticationRequired, q, &QWebSocketServer::preSharedKeyAuthenticationRequired); + QObject::connect(pSslServer, &QSslServer::alertSent, + q, &QWebSocketServer::alertSent); + QObject::connect(pSslServer, &QSslServer::alertReceived, + q, &QWebSocketServer::alertReceived); + QObject::connect(pSslServer, &QSslServer::handshakeInterruptedOnError, + q, &QWebSocketServer::handshakeInterruptedOnError); } #else qFatal("SSL not supported on this platform."); @@ -430,24 +436,50 @@ void QWebSocketServerPrivate::handshakeReceived() //This is a bug in FireFox (see https://bugzilla.mozilla.org/show_bug.cgi?id=594502) // According to RFC822 the body is separated from the headers by a null line (CRLF) - if (!pTcpSocket->peek(pTcpSocket->bytesAvailable()).endsWith(QByteArrayLiteral("\r\n\r\n"))) { + const QByteArray& endOfHeaderMarker = QByteArrayLiteral("\r\n\r\n"); + + const qint64 byteAvailable = pTcpSocket->bytesAvailable(); + QByteArray header = pTcpSocket->peek(byteAvailable); + const int endOfHeaderIndex = header.indexOf(endOfHeaderMarker); + if (endOfHeaderIndex < 0) { + //then we don't have our header complete yet + //check that no one is trying to exhaust our virtual memory + const qint64 maxHeaderLength = MAX_HEADERLINE_LENGTH * MAX_HEADERLINES + endOfHeaderMarker.size(); + if (Q_UNLIKELY(byteAvailable > maxHeaderLength)) { + pTcpSocket->close(); + setError(QWebSocketProtocol::CloseCodeTooMuchData, + QWebSocketServer::tr("Header is too large.")); + } return; } + const int headerSize = endOfHeaderIndex + endOfHeaderMarker.size(); + disconnect(pTcpSocket, &QTcpSocket::readyRead, this, &QWebSocketServerPrivate::handshakeReceived); bool success = false; bool isSecure = (m_secureMode == SecureMode); - if (m_pendingConnections.length() >= maxPendingConnections()) { + if (Q_UNLIKELY(m_pendingConnections.length() >= maxPendingConnections())) { pTcpSocket->close(); - pTcpSocket->deleteLater(); setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection, QWebSocketServer::tr("Too many pending connections.")); return; } + //don't read past the header + header.resize(headerSize); + //remove our header from the tcpSocket + qint64 skippedSize = pTcpSocket->skip(headerSize); + + if (Q_UNLIKELY(skippedSize != headerSize)) { + pTcpSocket->close(); + setError(QWebSocketProtocol::CloseCodeProtocolError, + QWebSocketServer::tr("Read handshake request header failed.")); + return; + } + QWebSocketHandshakeRequest request(pTcpSocket->peerPort(), isSecure); - QTextStream textStream(pTcpSocket); + QTextStream textStream(header, QIODevice::ReadOnly); request.readHandshake(textStream, MAX_HEADERLINE_LENGTH, MAX_HEADERLINES); if (request.isValid()) { @@ -461,16 +493,16 @@ void QWebSocketServerPrivate::handshakeReceived() supportedProtocols(), supportedExtensions()); - if (response.isValid()) { + if (Q_LIKELY(response.isValid())) { QTextStream httpStream(pTcpSocket); httpStream << response; httpStream.flush(); - if (response.canUpgrade()) { + if (Q_LIKELY(response.canUpgrade())) { QWebSocket *pWebSocket = QWebSocketPrivate::upgradeFrom(pTcpSocket, request, response); - if (pWebSocket) { + if (Q_LIKELY(pWebSocket)) { finishHandshakeTimeout(pTcpSocket); addPendingConnection(pWebSocket); Q_EMIT q->newConnection(); @@ -501,11 +533,13 @@ void QWebSocketServerPrivate::handleConnection(QTcpSocket *pTcpSocket) const QObjectPrivate::connect(pTcpSocket, &QTcpSocket::readyRead, this, &QWebSocketServerPrivate::handshakeReceived, Qt::QueuedConnection); - if (pTcpSocket->canReadLine()) { - // We received some data! We must emit now to be sure that handshakeReceived is called - // since the data could have been received before the signal and slot was connected. - emit pTcpSocket->readyRead(); + + // We received some data! We must emit now to be sure that handshakeReceived is called + // since the data could have been received before the signal and slot was connected. + if (pTcpSocket->bytesAvailable()) { + Q_EMIT pTcpSocket->readyRead(); } + QObjectPrivate::connect(pTcpSocket, &QTcpSocket::disconnected, this, &QWebSocketServerPrivate::onSocketDisconnected); } |