Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-65960

QNetworkAccessManager.clearConnectionCache() wrongly flushes data on aborted requests.

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 5.11.2
    • 5.10.0
    • Network: SSL
    • 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"

       

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            manordheim Mårten Nordheim
            llsag85dywftnhuo Jan Murawski
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes