didil
didil

Reputation: 715

What's the best way to delete a QNetworkReply?

I'm writing an embedded RESTful API client using the Qt5 Embedded layer of OpenEmbedded project. I want my client to be able to send a simple async request to notify my server on what's going on and an other scenario is that my client needs to synchronize data from the server.

To do so I wrote two functions, one to send the request and another to get the response. So if I don't care about the response, I only use the first one.

To be able to use the response with the second one, I use a QNetworkReply * as a member of my class. To keep the QNetworkReply alive, I also set the QNetworkAccessManager as a member of my class.

#include <QtNetwork>

class ApiClient : public QObject
{
    Q_OBJECT

public:

    ApiClient(QObject *parent = 0);

private:

    QString host;
    QString key;
    quint32 replyTimeout;
    QNetworkAccessManager manager;
    QNetworkReply *reply;

    void sendRequest(const QString &method, const QVariantMap &params = QVariantMap());
    QVariantMap getResponse() const;
};

The apiclient.cpp file:

#include "apiclient.h"

ApiClient::ApiClient(QObject *parent) : QObject(parent)
{

}

void ApiClient::sendRequest(const QString &method, const QVariantMap &params)
{
    QUrl url(QString("%1/%2/").arg(host).arg(method));

    QUrlQuery query;
    query.addQueryItem("key", key);

    if (!params.empty())
    {
        QMapIterator<QString, QVariant> it(params);
        while (it.hasNext())
        {
            it.next();
            query.addQueryItem(it.key(), it.value().toString());
        }
    }

    url.setQuery(query);
    qDebug() << "url: " << url.toString();

    reply = manager.get(QNetworkRequest(url));
}

QVariantMap ApiClient::getResponse() const
{
    if (!reply)
    {
        qFatal("No request sent!");
    }

    QTimer timer;
    timer.setSingleShot(true);

    QEventLoop loop;
    connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    timer.start(replyTimeout);
    loop.exec();

    if (timer.isActive())
    {
        timer.stop();

        if (reply->error())
        {
            qFatal("Wrong reply!");
        }

        int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        if (code != 200)
        {
            qFatal("Invalid server response!");
        }

        QJsonDocument result = QJsonDocument::fromJson(reply->readAll());

        if (result.isNull())
        {
            qFatal("Invalid JSON!");
        }

        reply->deleteLater();
        return result.object().toVariantMap();
    }

    disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    reply->abort();
    reply->deleteLater();
    return QVariantMap();
}

Is that a good way to proceed? How should I manage the QNetworkReply pointer when other signals are emitted (i.e. error(), sslErrors(), ...)?

Upvotes: 0

Views: 1785

Answers (1)

Kevin Krammer
Kevin Krammer

Reputation: 5207

QNetworkReply will always emit finished(), even when an error occured.

deleteLater() could even be called in a slot connected to that signal, so that part should be fine.

But I would recommend to look into a more asynchronous approach of handling the request, nested event loops like the on in your getResponse() can lead to "interesting" behavior, because you can basically get into re-entrancy situations in a single threaded program.

Upvotes: 1

Related Questions