diff options
author | Heiko Voigt <hvoigt@hvoigt.net> | 2019-03-14 17:53:07 +0100 |
---|---|---|
committer | Heiko Voigt <hvoigt@hvoigt.net> | 2019-04-29 16:28:50 +0000 |
commit | 2e54dbe86eac61e87782a138dbcc158cb6b10cd9 (patch) | |
tree | 0e2e813d54c7f4e51798868d870a65bc252d0848 /src/websockets/qwebsocketserver_p.cpp | |
parent | 240f14a7a7b56c3eb6c2bdd34ab9c9ee4bca2990 (diff) | |
download | qtwebsockets-2e54dbe86eac61e87782a138dbcc158cb6b10cd9.tar.gz |
websocket server: add timeout to abort incomplete handshakes
A websocket connection can involve two types of handshakes. First an
optional SSL handshake and second the websocket handshake itself. Either
one can get stalled/stuck if the other side does not answer.
To be robust by default and for easy mitigation by users of websockets
let's introduce a handshake timeout. We introduce a default timeout of 10
seconds which can be customized by the newly introduced setHandshakeTimeout()
method.
One major location where connections got stuck was when the connection
queue was filled with connections waiting for the SSL handshake. Only
connections that have finished this handshake can be processed anyway so
we now add them to the queue once they are fully ready to start the
websocket handshake.
Task-number: QTBUG-63312
Task-number: QTBUG-57026
Change-Id: Ia286221f1d8da1000e98973496280fde16ed811d
Reviewed-by: Alf Crüger <a.crueger@baxi-innotech.de>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/websockets/qwebsocketserver_p.cpp')
-rw-r--r-- | src/websockets/qwebsocketserver_p.cpp | 37 |
1 files changed, 34 insertions, 3 deletions
diff --git a/src/websockets/qwebsocketserver_p.cpp b/src/websockets/qwebsocketserver_p.cpp index 4c3eeaf..8225b59 100644 --- a/src/websockets/qwebsocketserver_p.cpp +++ b/src/websockets/qwebsocketserver_p.cpp @@ -49,6 +49,7 @@ #include "qwebsocket_p.h" #include "qwebsocketcorsauthenticator.h" +#include <QtCore/QTimer> #include <QtNetwork/QTcpServer> #include <QtNetwork/QTcpSocket> #include <QtNetwork/QNetworkProxy> @@ -73,7 +74,8 @@ QWebSocketServerPrivate::QWebSocketServerPrivate(const QString &serverName, m_pendingConnections(), m_error(QWebSocketProtocol::CloseCodeNormal), m_errorString(), - m_maxPendingConnections(30) + m_maxPendingConnections(30), + m_handshakeTimeout(10000) {} /*! @@ -97,6 +99,8 @@ void QWebSocketServerPrivate::init() QObjectPrivate::connect(pSslServer, &QSslServer::newEncryptedConnection, this, &QWebSocketServerPrivate::onNewConnection, Qt::QueuedConnection); + QObjectPrivate::connect(pSslServer, &QSslServer::startedEncryptionHandshake, + this, &QWebSocketServerPrivate::startHandshakeTimeout); QObject::connect(pSslServer, &QSslServer::peerVerifyError, q, &QWebSocketServer::peerVerifyError); QObject::connect(pSslServer, &QSslServer::sslErrors, @@ -381,8 +385,12 @@ void QWebSocketServerPrivate::setError(QWebSocketProtocol::CloseCode code, const */ void QWebSocketServerPrivate::onNewConnection() { - while (m_pTcpServer->hasPendingConnections()) - handleConnection(m_pTcpServer->nextPendingConnection()); + while (m_pTcpServer->hasPendingConnections()) { + QTcpSocket *pTcpSocket = m_pTcpServer->nextPendingConnection(); + if (Q_LIKELY(pTcpSocket) && m_secureMode == NonSecureMode) + startHandshakeTimeout(pTcpSocket); + handleConnection(pTcpSocket); + } } /*! @@ -463,6 +471,7 @@ void QWebSocketServerPrivate::handshakeReceived() request, response); if (pWebSocket) { + finishHandshakeTimeout(pTcpSocket); addPendingConnection(pWebSocket); Q_EMIT q->newConnection(); success = true; @@ -502,4 +511,26 @@ void QWebSocketServerPrivate::handleConnection(QTcpSocket *pTcpSocket) const } } +void QWebSocketServerPrivate::startHandshakeTimeout(QTcpSocket *pTcpSocket) +{ + if (m_handshakeTimeout < 0) + return; + + QTimer *handshakeTimer = new QTimer(pTcpSocket); + handshakeTimer->setSingleShot(true); + handshakeTimer->setObjectName(QStringLiteral("handshakeTimer")); + QObject::connect(handshakeTimer, &QTimer::timeout, [=]() { + pTcpSocket->close(); + }); + handshakeTimer->start(m_handshakeTimeout); +} + +void QWebSocketServerPrivate::finishHandshakeTimeout(QTcpSocket *pTcpSocket) +{ + if (QTimer *handshakeTimer = pTcpSocket->findChild<QTimer *>(QStringLiteral("handshakeTimer"))) { + handshakeTimer->stop(); + delete handshakeTimer; + } +} + QT_END_NAMESPACE |