Qt SSL: Handshake failed for requests made from QML - qt

I make some https requests directly from my qml views, for instance for image sources. As I have a self signed certificate server side, I need to tell qt to ignore some ssl errors (I control both the server and the client applications, so this shouldn't really be a problem).
I've made a QQmlNetworkAccessManagerFactory to create NAMs, where I connect to the sslErrors signal.
UltraQmlAccessManagerFactory.h:
#ifndef FACKFACKTORy_H
#define FACKFACKTORy_H
#include <QQmlNetworkAccessManagerFactory>
#include <QObject>
#include <QNetworkReply>
#include <QList>
#include <QSslError>
#include <QNetworkAccessManager>
#include <QDebug>
#include <QSslCertificate>
class UltraQmlNetworkAccessManagerFactory : public QObject,
public QQmlNetworkAccessManagerFactory {
Q_OBJECT
private:
QNetworkAccessManager* nam;
QList<QSslError> expectedSslErrors;
public:
explicit UltraQmlNetworkAccessManagerFactory();
~UltraQmlNetworkAccessManagerFactory();
virtual QNetworkAccessManager* create(QObject* parent);
public slots:
void onIgnoreSslErrors(QNetworkReply* reply, QList<QSslError> errors);
};
#endif
UltraQmlNetworkAccessManagerFactory.cpp:
#include "UltraQmlNetworkAccessManagerFactory.h"
UltraQmlNetworkAccessManagerFactory::UltraQmlNetworkAccessManagerFactory() {
}
UltraQmlNetworkAccessManagerFactory::~UltraQmlNetworkAccessManagerFactory() {
delete nam;
}
QNetworkAccessManager* UltraQmlNetworkAccessManagerFactory::create(QObject* parent) {
QNetworkAccessManager* nam = new QNetworkAccessManager(parent);
QObject::connect(nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
this, SLOT(onIgnoreSslErrors(QNetworkReply*,QList<QSslError>))
);
return nam;
}
void UltraQmlNetworkAccessManagerFactory::onIgnoreSslErrors(QNetworkReply *reply, QList<QSslError> errors) {
for (int i = 0; i < errors.size(); i++) {
qDebug() << "e: " << errors.at(i) << endl;
}
reply->ignoreSslErrors(errors);
}
There is also some glue in main.cpp that sets this factory to be used, I doubt that part is a source of errors as the qDebug prints are visible in the output.
As can be seen in the .cpp file in the function/slot onIgnoreSslErrors, I try to ignore every error (as a test) that I receive, but in the output I do not get the expected results.
Output
e: "The certificate is self-signed, and untrusted"
qrc:/qml/file/ImageView.qml:16:5: QML Image: SSL handshake failed
I have successfully made QNetworkRequests from C++ directly with a QSslConfiguration, specifying TLSV1_0 and a certificate. As I have a suspicion that the handshake fails because one side expects SSL and the other TLS I have also tried to set the QSslConfiguration on the QNetworkRequest object throgh reply->request(); This, however, changes nothing.

(This is a very old one but as I stumbled over this recently and haven't found the answer, I think it's still worth answering)
You don't show the place where you actually setup the factory object but it is extremely likely that it does not belong to the same (actually, any) thread used when its create() method is called. Here's an excerpt from the Qt documentation on the class:
the developer should be careful if the signals of the object to be returned from create() are connected to the slots of an object that may be created in a different thread
It further mentions authenticationRequired() signal but sslErrors() acts in the same way: both signals, among a few others, need either a direct or a blocking queued connection so that by the time of returning to the place of emitting the signal the network reply object has already been configured by the slot.
What happens in your case is (very likely) the following (TL;DR: your slot is called asynchronously by a queued connection because it lives in a different thread, while sslErrors() requires a synchronous change to the running network reply object; despite the order of log lines, the request fails first and ignoreSslErrors() is called later):
The factory object is created, and the QML engine configured, in the main thread.
QML engine spawns a few threads to perform backend stuff, notably network requests for URLs (I'm making the assumption here that your ImageView.qml has an Image component). To perform the network request, these threads call UltraQmlNetworkAccessManagerFactory::create().
create() produces a NAM object and sets up a connection on it. The parent here is either the QQmlEngine object or (specifically for image requests) the backend thread object, as you can see e.g. here. Therefore, this NAM object belongs to the backend thread.
connect() uses Qt::AutoConnection type by default, which, since the threads of the factory and of the NAM object are different, corresponds to Qt::QueuedConnection. As a sidenote, the threads are checked at the time of signal invocation.
Eventually a QNetworkAccessManager::sslErrors() signal is emitted. Since this is a queued connection, the only thing that immediately happens is placing an invocation of onIgnoreSslErrors() on the event queue for the main thread.
If you're very lucky, you may have a context switch to the main thread right after that - but there's literally nothing to ensure that so it's much more likely that control returns to the site where QNetworkAccessManager::sslErrors() was emitted. And since ignoreSslErrors() wasn't called, the request fails the handshake. The backend thread posts relevant data from the (failed) QNetworkReply object back to the main thread - see the postReply call in the middle of the method (or it may do it a bit later - that doesn't matter anymore).
Once the context switches to the main thread ignoreSslErrors() is executed - alas, it's already too late, as the network reply has very likely finished with failure already; but that first log line comes out now.
The main thread goes on through the event loop and finds the QQuickPixmapReply::Event object with the failure data. After some unroll of the calls and signals the failed image ends up in QQuickImageBase::requestFinished() that prints the second log line for you .
As for the fix, it's tempting to just specify Qt::BlockingQueuedConnection as the fifth parameter to connect(). Unfortunately, this will deadlock if a request is ever made from QQmlEngine that runs in the main thread - and uses a NAM instance created by this factory to, e.g., request QML components over the network. Best I could come out with so far is an equivalent of
connect(nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
this, SLOT(onIgnoreSslErrors(QNetworkReply*,QList<QSslError>)),
currentThread() == this->thread() ? Qt::DirectConnection
: Qt::BlockingQueuedConnection
);

Related

Why the task of QThread can not be executed until the function returns

For example:
void MainWidget::testThreadTask()
{
qDebug() << "On test task";
}
void MainWidget::onBtnClick()
{
QThread *thread = new QThread;
connect(thread, QThread::started, this, testThreadTask);
thread->start();
qDebug() << "Thread START, now we wait 5s";
QElapsedTimer timer;
timer.start();
while (timer.elapsed() < 5000)
{
}
qDebug() << "END";
}
The program output is:
START wait 5s
END
On test task
I want to create a task to handle something after the button is pressed, and then the function will wait for the task to complete before returning.
In fact, it may not be necessary to create a new task and wait for it to execute, because since you have to wait and get stuck there, why not run it directly in the function.
But this is actually a problem when I deal with QT serial data. I want to send the data to the serial port after pressing the button, and then wait for the data (by constantly reading), but I find that when I have been waiting, the serial port can not read the data at all, only when I exit the function the serial port can read the data.
Is there any way to deal with serial data sending and receiving synchronization?
void MainWidget::onBtnClick()
{
serial->write("Test");
if (serial->bytesAvailable())
{
QByteArray data = serialIo->readAll();
// handle the data
}
}
You are mistaken with what is happening in your application. I suggest you read Threads and QObjects (the entire page), Qt::ConnectionType and the detailed description of QThread.
What is happening to you is:
MainWidget does not live in thread. For the slot of a regular object to be called from thread, it first needs to be moved to that thread.Note that subclasses of QWidget cannot be moved to another thread. Because some OS supported by Qt limit where windows can live, they made the choice to force all QWidget to stay in the main thread, in all OS Qt can execute on.
When you connect thread to this (which BTW is incorrect in your question, it should have been with ampersands connect(thread, &QThread::started, this, &MainWidget::testThreadTask);), you create a queued connection, even though the thread has not technically started yet.
When you start the thread:
It fires its started signal.
Because the connection is a Qt::QueuedConnection, the slot will only be executed after returning to the main thread's event loop, i.e. some time after returning from onBtnClick.
Notes:
You would have more useful information in qDebug() about the threads running your code by using QThread::currentThread().Even better than that, your IDE should provide you a window specifically to see what thread has reached a breakpoint (Ctrl+Alt+H on Visual Studio).
At the risk of insisting, keep in mind this warning from the Qt help:
Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.
With that said, because you wait 5 seconds before returning to the event loop and because it is only test code (= there should be no bug + it does not matter even if there is one), you should try to create a Qt::DirectConnection, just to see the slot be invoked from the worker thread.
The detailed description of QThread (link above) shows a complete working example of a worker object being moved to the new thread before it is started. The point is:
A worker object is created, then moved to the worker thread.
Connections are created for the controller to send QString to the worker object via signal/slot and for the worker object to return result to the controller via signal/slot too.
All these connections are Qt::QueuedConnection by default since the worker object was moved.
The worker thread is started. Since run was not overriden, it starts an event loop (in exec).
And there you have it.
Remember 1 things: widgets cannot be moved!!! Create your own worker object.

Detect QApplication exit as normal or crash

I need to detect the application get exit as normal or crash. QProcess have the finished() signal and can get the exit code. But i need this exit code for QApplication when the application get crash or close.
When your process crashes, it's gone. The crash means that the process has finished because of an unhandled exception. Your job should be to prevent the crash from happening. In other words: handle the exceptions. Note that the exceptions may not be C++ exceptions, they may be low-level platform-specific mechanisms, such as native exceptions on Windows or signals on UNIX. You'd have to handle those, but recognize that the underlying issue is not fixed merely because you catch such an exception. You must assume that the state of your application has been corrupted, and the only safe thing to do is to exit ASAP anyway. For example, do not try to modify any files: you're likely to corrupt them.
I don't think this is something you can do just like that. Reading the value returned by QApplication::exec() is related to the Qt infrastructure:
Enters the main event loop and waits until exit() is called, then
returns the value that was set to exit() (which is 0 if exit() is
called via quit()).
Usually your main looks like this:
#include <QApplication>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
// Initialize your widget(s)
return a.exec(); // You can store this and check its value
}
However if I'm not mistaken this doesn't include handling a crash of your application (segmentation fault, unhandled exception etc.). In Linux people usually use a script which starts the application and then reads its exit code after the application quits or crashes. If you use Linux you can use echo $? to read the exit code from the bash scrip (or its equivalent for a different shell) and then do something based on its value.
Note also that you can at least do some exception handling since some crashes result in exactly that - an exception that has been thrown for some reason and has not been processed properly. Unhandled exceptions in Qt get propagated to the top level (that is QCoreApplication).

QTimer fires late due other thread busy with big loop

Brief of environment:
I have a device on which runs an application written in qt. It has a main thread which handles Database operations (SQlite) and a separate thread for networking operations (via 3G).
Mains thread Event loop is ran by QCoreApplication::exec and the other thread which handles networking operations is ran by QThread::exec. Btw, socket thread affinity is changed after it's started (Eg. moveToThread(socketThreadPtr))
Brief of problem:
Main thread is busy in a loop, in order to select around 10k records from database and that loop takes about +30 seconds. In the network thread there is a 15 seconds timer which has to send a keep alive message each time expires. The problem is that the slot for timeout() signal is executed only after the loop is finished.
Solution founded until now(but not satisfying):
If I call QCoreApplication::processEvents in the loop that selects the records, problem is solved, but I wonder if a solution exists instead of this workaround.
Remark:
The timer, signal and slot, which gives the command to send the keep alive message is currently handled in the main thread( but the read/write happens in the network thread). Also, I moved the timer on the network thread but I got the same result as being on the main thread.
You have to create timer in your network thread, maybe Qtimer is a member of network thread, so the network thread will be constructed in main thread and thread affinity of its children's
set to main thread, next you have moved network thread to new Qthread, but what about Qtimer? it still lives in main thread (except when you explicitly define network class as it's parent, so as you said moveToThread will affect object's children as well as Qtimer)
The Qtimer should be constructed in network thread, you can construct a new Qtimer in one of the network thread slots and connecting it to the Qthread::start signal. So by calling start method of Qthrad your slot will be executed on new thread and Qtimer will be constructed on that thread respectively.
Something like this should actually create the socket and the timer in your dedicated thread, given your main-thread is the server and clients should be handled in threads. Otherwise just use QTcpSocket::connectToHost() or QTcpServer:::bind()in your initialize function.
Mainthread:
auto t = new QThread();
t->start();
auto o = new MyThreadObject();
o.moveToThread(t);
o.setDescriptor(socketDesc);
QMetaObject::invokeMethod(o, "initialize", Qt::QueuedConnection);
MyThreadObject:
class MyThreadObject : public QObject
{
Q_OBJECT
public:
MyThreadObject(){...};
void setDescriptor(qintptr socketdescriptor)
{
m_desc = socketdescriptor;
}
public slots:
void initialize()
{
m_tcpSocket = new QTcpSocket();
m_tcpSocket->setSocketDescriptor(m_desc);
//socket configuration
m_timer = new QTimer();
//timer configuration
m_timer->start();
}
private:
QTcpSocket* m_tcpSocket;
QTimer* m_timer;
qintptr m_desc;
}

QTimer timeout problems with QEventLoop and QNAM

I have created my own HTTP class that utilizes QNAM and provides means for sending HTTP requests. It uses QEventLoop for synchronization and QTimer for timeouts.
I'm facing few problems with my solution. On certain Symbian platforms my QTimer signals timeout too fast (e.g. like after 1 sec when timeout is 30 secs). This happends usually if my HTTP Post playload is large or if I'm downloading a file via GET (request takes some time to complete). I want to note that same code works fine on certain devices (S60 3rd ed.) but on the other hand some devices (5th edition) get this error almost all the time.
Here is a code snippet:
MyHttp::MyHttp(QObject *parent) : QObject(parent)
{
m_Timer.setSingleShot(true);
connect(&m_Manager, SIGNAL(finished(QNetworkReply*)), SLOT(OnFinished(QNetworkReply*)));
connect(&m_Timer, SIGNAL(timeout()), SLOT(OnTimeout()));
}
void MyHttp::Post(const QString &data)
{
m_RetCode = 0;
QNetworkRequest request(url);
m_Reply = m_Manager.post(request, data.toAscii()); // QPointer<QNetworkReply> m_Reply
m_Timer.start(30*1000);
m_EventLoop.exec(); // Synchronization point
}
void MyHttp::OnFinished(QNetworkReply * reply)
{
// Handle response / Timeout / Errors
reply->deleteLater(); // Use deleteLater() as adviced in the documentation
StopWaiting();
}
void MyHttp::StopWaiting()
{
m_Timer.stop();
m_EventLoop.exit();
}
void MyHttp::OnTimeout()
{
m_RetCode = TIMEOUT; // #define TIMEOUT 50000
if(m_Reply.isNull() == false)
{
// Abort reply
m_Reply->abort();
}
}
Personally I think that one of the following might cause the problem:
re-entering local event loop messes up the signals
I'm utilizing same QNAM multiple times (several request during same session). This is required because if I destroy the QNAM my session goes down on the server side.
Is anyone able to see some errors that might cause this behavior?
Platform: Symbian S60 3rd/5th edition
Tools: Nokia Qt SDK
I've exactly that sort of problems as well. Using local to the method QEventLoop produces strange results, like blocking some events to be processed (and then loop never exits), or like explained, provoking QTimer to fire too fast before timeout (and then loop exits too early).
Using an instance field for the loop initialized once in the constructor of parent object of the loop, seems to solve the problem.
I'm on Qt 4.6.3 and Symbian S60/5th Edition.

Qt/C++ event loop exception handling

I am having an application heavily based on QT and on a lot of third party libs. These happen to throw some exceptions in several cases.
In a native Qt App this causes the application to abort or terminate. Often the main data model is still intact as I am keeping it in pure Qt with no external data.
So I am thinking that I could also just recover by telling the user that there has occurred an error in this an that process and he should save now or even decide to continue working on the main model.
Currently the program just silently exits without even telling a story.
Sometimes it's really hard to catch all exception. If one exception accidently slips through, the following helps a lot. Inherit from QApplication and override the notify() function in the following way
bool MyApplication::notify(
QObject * receiver,
QEvent * event )
{
try
{
return QApplication::notify(receiver, event);
}
catch(...)
{
assert( !"Oops. Forgot to catch exception?" );
// may be handle exception here ...
}
return false;
}
Then replace the QApplication in your main() function by your custom class. All events and slots are issued through this function, so that all exceptions can be caught and your application becomes stable.
As stated in the Qt documentation here, Qt is currently not fully exception safe. The "Recovering from exceptions" section on that page describes the only thing which you can do in a Qt application when an exception is thrown - clean up and exit the app.
Given that you are using third party libraries which do throw exceptions, you need to catch these at the boundary between the external library and the Qt code, and handle them there - as stated in Caleb's comment. If the error must be propagated into the Qt application, this must be done either by returning an error code (if possible), or by posting an event.

Resources