Nulik Nulik - 13 days ago 6
C++ Question

How to make QNetworkReply to return custom data?

I am going to use QNetworkAccessManager to make requests to HTTP server by my mobile app to the server. The question is, how do you link custom data to each request ? I tried to subclass QNetworkReply but I found out that I have to implement virtual methods

but I don't know what those should return so I am afraid I am going to break network request functionality.

For example, when my app does the log in procedure, it has to store the email address of the account:

class MyApp : public QObject

QNetworkRequest request;
QNetworkReply *reply;
QNetworkAccessManager *manager;



void MyApp::do_log_in(QString email, QString password) {
QString s;; // <-- I have to store email address before sending request to server, but where do I store it?


void MyApp::login_finished(QNetworkReply *rep) {
DepservReply *reply;
QString email;
email= ...... // <-- I need to get the email address from QNetworkReply object somehow
///my code here handling server reply

So, how do I implement storage and retrieval of email in my case, what classes should I subclass and what methods should I re-implement ?


You can leverage the dynamic property system available in each QObject and hold the data in the reply:

#include <QtNetwork>

class MyCtl : public QObject
    QNetworkAccessManager manager{this};
    // ...
    void reply_finished(QNetworkReply *reply);
    MyCtl(QObject *parent = nullptr);
    void do_log_in(const QString &email, const QString &password);

static const char kAuthGetSalt[] = "req_auth-get-salt";
static const char kDoAuth[] = "req_do-auth";
static const char kEmail[] = "req_email";
static const char kPassword[] = "req_password";

static const auto authGetSaltUrl = QStringLiteral("");
static const auto doAuthUrl = QStringLiteral("");

MyCtl::MyCtl(QObject *parent) : QObject{parent}
    connect(&manager, &QNetworkAccessManager::finished, this, &MyCtl::reply_finished);

void MyCtl::do_log_in(const QString &email, const QString &password) {
    auto url = authGetSaltUrl.arg(email);
    auto reply = manager.get(QNetworkRequest{url});
    reply->setProperty(kAuthGetSalt, true);
    reply->setProperty(kEmail, email);
    reply->setProperty(kPassword, password);

void MyCtl::reply_finished(QNetworkReply *reply) {
    if (!reply->property(kAuthGetSalt).isNull()) {
        reply->deleteLater(); // let's not leak the reply
        if (reply->error() == QNetworkReply::NoError) {
            auto salt = reply->readAll();
            auto email = reply->property(kEmail).toString();
            auto password = reply->property(kPassword).toString();
            Q_ASSERT(!password.isEmpty() && !email.isEmpty());
            QCryptographicHash hasher{QCryptographicHash::Sha1};
            hasher.addData(salt); // the server must hash the same way
            auto hash = hasher.result().toBase64(QByteArray::Base64UrlEncoding);
            auto url = doAuthUrl.arg(email).arg(QString::fromLatin1(hash));

            auto reply = manager.get(QNetworkRequest{url});
            reply->setProperty(kDoAuth, true);
            reply->setProperty(kEmail, email);
    else if (!reply->property(kDoAuth).isNull()) {
        if (reply->error() == QNetworkReply::NoError) {
            auto email = reply->property(kEmail).toString();
            // ...

Use a constant for a property name to avoid typos by letting the compiler check that you're using a valid identifier.

The example above rectifies the following critical safety issues in your code:

  1. Sending security credentials over a clear connection: use https://, not http://.

  2. Sending a password in cleartext: instead, send a salted hash of it. Your server should generate a random salt for each account when the accounts are created. Existing accounts can be left unsalted, but they should acquire a salt as soon as the user changes the password.

Also note that a QString to QUrl conversion will automatically percent-encode the string, so doing it explicitly is unnecessary.