Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QNetworkReply destroyed before completing



  • Hi guys,

    Quick disclaimer I've been at this for days and am probably overlooking something. I am also learning as I go so every chance I'm doing it very wrong. I've learned enough about QNetworkAccessManager etc to be dangerous but not competent :)

    The problem I have at the moment is that I need to run various get/post api calls to web services, which works fine if the code runs in a cli window but not a gui (QWidget) window. to narrow down the problem I created a test project with code snippets from tutorials:

    worker.h

    #ifndef WORKER_H
    #define WORKER_H
    
    #include <QObject>
    #include <QtDebug>
    #include <QNetworkAccessManager>
    #include <QNetworkReply>
    #include <QNetworkRequest>
    #include <QAuthenticator>
    #include <QNetworkProxy>
    
    class worker : public QObject
    {
        Q_OBJECT
    public:
        explicit worker(QObject *parent = nullptr);
    
    signals:
    
    public slots:
        void get(QString location);
        void post(QString location, QByteArray data);
    private slots:
            void readyRead();
            void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
            void encrypted(QNetworkReply *reply);
            void finished(QNetworkReply *reply);
            void networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
            void preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator);
            void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
            void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
    
    private:
    
            QNetworkAccessManager manager;
    
    };
    
    #endif // WORKER_H
    
    
    

    worker.cpp

    #include "worker.h"
    
    worker::worker(QObject *parent) : QObject(parent)
    {
        connect(&manager,&QNetworkAccessManager::authenticationRequired,this,&worker::authenticationRequired);
        connect(&manager,&QNetworkAccessManager::encrypted,this,&worker::encrypted);
        connect(&manager,&QNetworkAccessManager::networkAccessibleChanged,this,&worker::networkAccessibleChanged);
        connect(&manager,&QNetworkAccessManager::preSharedKeyAuthenticationRequired,this,&worker::preSharedKeyAuthenticationRequired);
        connect(&manager,&QNetworkAccessManager::proxyAuthenticationRequired,this,&worker::proxyAuthenticationRequired);
        connect(&manager,&QNetworkAccessManager::sslErrors,this,&worker::sslErrors);
    }
    
    void worker::get(QString location)
    {
        qInfo() << "getting from server...";
        QNetworkReply* reply = manager.get(QNetworkRequest(QUrl(location)));
        connect(reply,&QNetworkReply::readyRead,this,&worker::readyRead);
    }
    
    void worker::post(QString location, QByteArray data)
    {
        qInfo() << "posting to server...";
        QNetworkRequest request = QNetworkRequest (QUrl(location));
        request.setHeader(QNetworkRequest::ContentTypeHeader,"text/plain");
        QNetworkReply* reply = manager.post(request,data);
        connect(reply,&QNetworkReply::readyRead,this,&worker::readyRead);
    }
    
    void worker::readyRead()
    {
        qInfo() << "ReadyRead";
        QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
        if(reply) qInfo () <<reply->readAll();
    }
    
    void worker::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
    {
        Q_UNUSED(reply)
        Q_UNUSED(authenticator)
        qInfo() << "authenticationRequired";
    }
    
    void worker::encrypted(QNetworkReply *reply)
    {
        Q_UNUSED(reply)
        qInfo() << "encrypted";
    }
    
    void worker::finished(QNetworkReply *reply)
    {
        Q_UNUSED(reply)
        qInfo() << "finished";
    }
    
    void worker::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
    {
        Q_UNUSED(accessible)
        qInfo() << "networkAccessibleChanged";
    }
    
    void worker::preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator)
    {
        Q_UNUSED(reply)
        Q_UNUSED(authenticator)
        qInfo() << "preSharedKeyAuthenticationRequired";
    }
    
    void worker::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
    {
        Q_UNUSED(proxy)
        Q_UNUSED(authenticator)
        qInfo() << "proxyAuthenticationRequired";
    }
    
    void worker::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
    {
        Q_UNUSED(reply)
        Q_UNUSED(errors)
        qInfo() << "sslErrors";
    }
    
    

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
    };
    
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp

    
    
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "worker.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        worker Worker;
        QByteArray data;
        data.append("param1=hello");
        data.append("&");
        data.append("param2=world");
        Worker.post("https://postman-echo.com/post",data);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    

    now I know the reply is being destroyed by adding the folowing code at the end of the post function

        connect(reply, &QObject::destroyed,
                [] { qDebug() << "Sender got deleted!"; });
    

    for context the reason I'm attempting to call the functions within the mainwindow, is that there will be push buttons connected to functions that will download file, get a specific page etc.

    Thanks in advance!



  • @Laurentioos move worker Worker; to

    // ...
    private:
        Ui::MainWindow *ui;
        worker Worker;
    };
    

    read about od scope in variables



  • @eyllanesc I dont understand why I would move the variable to mainwindow.h?

    quick bit of reading though and I guess I misunderstood the parent child relationship. I had thought that Worker would be a child of MainWindow, and exist so long as MainWindow exists, where am I right to understand it will only exist within the context of the function because I'm storing the class in a variable?



  • @Laurentioos mmm, what you say about the relationship between QObjects is correct but creating a QObject inside a function that belongs to a class does not make the QObject a child of the class. In your case "Worker" is a local variable that will be destroyed as soon as the MainWindow constructor finishes executing. Please read https://doc.qt.io/qt-5/qobject.html#details.

    If you want it to be child then you must use a pointer and pass to this: worker *Worker = new worker(this);



  • Thanks @eyllanesc while I've not yet got this working in my main app I now understand why it wasn't. I think for the small size of this app I'll likely just declare the Worker variable outside the scope of any function within mainwindow.cpp

    I think I may also have some reading to do to properly understand your last suggestion re pointer and it's use case.


  • Lifetime Qt Champion

    Hi

    @Laurentioos said in QNetworkReply destroyed before completing:

    I think for the small size of this app I'll likely just declare the Worker variable outside the scope of any function within mainwindow.cpp

    The size of the app does not matter to do things correctly ;-)

    If anything, make it a member variable of your class as the first suggestion of @eyllanesc. Sprinkling your code with global variables is a bad idea in any case.



  • @SGaist I know you're right haha

    I was struggling to understand the first suggestion still though, am I right to understand including the worker.h and declaring "worker Worker;" within the MainWindow.h is the correct way to go?


  • Lifetime Qt Champion

    @Laurentioos said in QNetworkReply destroyed before completing:

    am I right to understand including the worker.h and declaring "worker Worker;" within the MainWindow.h is the correct way to go?

    yes. To be more precise: inside the MainWindow class:

    class MainWindow : ...
    {
    private:
          worker Worker;
    

Log in to reply