Details
-
Bug
-
Resolution: Done
-
P2: Important
-
5.10.0
-
None
-
Arch linux & Windows 10
-
2dfa41e0eac65f5772ec61364f9afd0ce49fecc7
Description
There are situations were it necessary to close a TLS connection, which is managed by QNetworkAccessManager , in a slot connected to the QNetworkReply::encrypted signal. Such a case is given when custom checks on the server certificate need to be performed.
Aborting the connection in the QNetworkReply::encrypted slot works as expected. No data is send to the server if the client invokes the abort() method on the reply.
However, if one calls clearConnectionCache() on the network manager after the abort() the request is wrongly send to the server.
The following code is a minimal example to reproduce this error. It uses the fluke .key and .cert from qtbase/tests/auto/network/ssl/qsslsocket/certs/ .
#include <QtNetwork> #include <QtTest> class SslServer : public QTcpServer { Q_OBJECT protected: void incomingConnection(qintptr socketDescriptor) { QSslSocket* socket = new QSslSocket(this); QFile file("/tmp/fluke.key"); QVERIFY(file.open(QIODevice::ReadOnly)); QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); QVERIFY(!key.isNull()); socket->setPrivateKey(key); QList<QSslCertificate> localCert = QSslCertificate::fromPath("/tmp/fluke.cert"); QVERIFY(!localCert.isEmpty()); QVERIFY(!localCert.first().isNull()); socket->setLocalCertificate(localCert.first()); socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState); socket->startServerEncryption(); addPendingConnection(socket); } }; class test_NetworkManager_bug : public QObject { Q_OBJECT private Q_SLOTS: void clearConnectionCacheAfterAbort() { SslServer server; server.listen(); QTRY_COMPARE(server.isListening(), true); QByteArray serverReadAll; server.connect(&server, &QTcpServer::newConnection, [&] { while (server.hasPendingConnections()) { QTcpSocket* client = server.nextPendingConnection(); connect(client, &QTcpSocket::readyRead, client, [ =, &serverReadAll] { // This slot must not be invoked! serverReadAll = client->readAll(); qDebug() << "server readAll" << serverReadAll; QByteArray resp("HTTP/1.1 200 OK\r\n" "Content-Length: 1\r\n" "Content-Type: text/html\r\n" "\r\n" "a"); client->write(resp); }); } }); QSignalSpy spyServerConnection(&server, &QTcpServer::newConnection); QNetworkAccessManager nm; QNetworkReply* reply = nm.get(QNetworkRequest(QUrl(QString("https://localhost:%1").arg(server.serverPort())))); reply->ignoreSslErrors(); connect(reply, &QNetworkReply::encrypted, [&] { reply->abort(); nm.clearConnectionCache(); // <- Triggers the bug. }); QSignalSpy spyEncrypted(reply, &QNetworkReply::encrypted); QTRY_COMPARE(spyEncrypted.count(), 1); QTimer loopTimer; loopTimer.setInterval(3000); loopTimer.setSingleShot(true); loopTimer.start(); QEventLoop loop; connect(&loopTimer, &QTimer::timeout, &loop, &QEventLoop::quit); connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), &loop, &QEventLoop::quit); loop.exec(); QTRY_COMPARE(spyServerConnection.count(), 1); QVERIFY(serverReadAll.isNull()); } }; QTEST_GUILESS_MAIN(test_NetworkManager_bug) #include "test_NetworkManager_bug.moc"