Cost of constructing an empty QSslCertificate

Submitted by mimec on 2011-05-11

Recently I've been trying to solve a problem in WebIssues which manifests itself as a short delay just before connecting to the server. The UI just freezes for about two seconds. It seemed strange because all network operations are asynchronous so there shouldn't be any delay. A few experiments involving the new QElapsedTimer class revealed that the entire time was spent in the constructor of the CommandManager, i.e. the class responsible for communication with the server.

At the first glance the constructor simply creates a QNetworkAccessManager object and connects to a few signals. But the construction of the network access manager only takes several milliseconds, so where's rest of the time spent? What is easy to forget is that a constructor of an object implicitly calls the default constructors of all its fields, if they have such constructors. Two such members caught my attention: a QSslCertificate and a QSslConfiguration fields which are used when connecting to the server via the HTTPS protocol.

Debugging through the code I found that the default constructor of QSslConfiguration does nothing interesting, but the constructor of QSslCertificate always calls the following function, even when an empty certificate is being constructed:

QSslSocketPrivate::ensureInitialized();

This innocent looking function loads the OpenSSL libraries, initializes all encryption algorithms, seeds the random generator and loads default ciphers and certificates. No wonder it takes whole two seconds! We should keep this in mind and avoid creating an instance of the certificate until it's really needed; it doesn't make sense to load the OpenSSL libraries if we're not using a secure connection at all.

But why does the command manager need a certificate field? It's because of the way it handles the sslErrors signal of the network access manager. Instead of simply cancelling the connection, a dialog box is displayed containing a list of errors and a button which shows the details of the certificate. The user can cancel the connection or ignore the errors and proceed. However if another command is sent to the server after some time, the connection may be closed and it needs to be reestablished, resulting in the sslErrors signal being triggered again.

Obviously we don't want to constantly bother the user with the same error message, that's why the command manager stores the certificate once it's presented to the user and all further errors related to that certificate are silently ignored.

The solution was simple: I replaced the QSslCertificate with QList<QSslCertificate>. The list is initially empty, so no instance of the certificate is created when the command manager is constructed. An additional benefit is that the program remembers all ignored certificates, not just the most recent one.

Obviously the delay will still occur at some point if the connection is secure, but that will happen while processing asynchronous events and the UI will be refreshed by that time, so there's a good chance that the user won't even notice the delay.