Search code examples
windows-10qt5qwebviewqnetworkaccessmanagerqnetworkreply

How do I make my own custom QNetworkReply?


I've been trying to create my own custom QNetworkReply class which return custom bytes from reply. I do return that class from my QNetworkAccessManager::createRequest() overload that I also did overload. My problem is, none of my function overloads are called and consequently the QWebView's request fail, as the reply is "corrupted". I've tried to take some inspiration from Qt tests lib's FakeReply but I fail to make mine work.

Here's my code:

class FakeReply : public QNetworkReply
{
    Q_OBJECT

public:
    FakeReply(const QNetworkRequest& request, QByteArray &data, QObject* parent = 0);
    ~FakeReply();
    QByteArray readAll();
    QByteArray read(qint64 maxSize);
    qint64 peek(char *buffer, qint64 maxlen);
    QByteArray peek(qint64 maxLen);
    qint64 size() const;
    virtual qint64 bytesAvailable() const;
    bool isFinished() const;
    virtual void abort();
    virtual void close();
    bool isSequential() const;
protected:
    qint64 readData(char*, qint64) override;
private:
    QByteArray data;
};

And the .cpp file:

FakeReply::FakeReply(const QNetworkRequest &request, QByteArray &bytes, QObject *parent)
    : QNetworkReply(parent),
      data(bytes)
{
    setOperation(QNetworkAccessManager::GetOperation);
    setRequest(request);
    setUrl(request.url());
    open(QIODevice::ReadOnly);
    setFinished(true);
    emit finished();
}

FakeReply::~FakeReply()
{
    close();
}

QByteArray FakeReply::readAll()
{
    qDebug() << "FakeReply::readAll() got called!";
    size_t len = data.length();
    const char *bytes = static_cast<const char*>(data.data());
    char *buffer = static_cast<char*>(malloc(len));
    ASSERT(buffer != NULL, "run out of memory");
    memcpy(buffer, bytes, len);
    QByteArray output;
    output.setRawData(buffer, len);
    data = data.remove(0, len);
    return output;
}

QByteArray FakeReply::read(qint64 maxSize)
{
    qDebug() << "QByteArray FakeReply::read() got called!";
    static char* buffer = nullptr;
    if(bytesAvailable() == 0) {
        return QByteArray();
    }
    size_t len = computeMaxNumberOfBytes(maxSize);
    if(buffer != nullptr) {
        free(buffer);
    }
    buffer = static_cast<char*>(malloc(len));
    ASSERT(buffer != NULL, "run out of memory");
    memcpy(buffer, data.data(), len);
    data = data.remove(0, len);
    QByteArray output;
    output.setRawData(buffer, len);
    return output;
}

qint64 FakeReply::peek(char *buffer, qint64 maxlen)
{
    qDebug() << "FakeReply::peek() got called!";
    size_t len = computeMaxNumberOfBytes(maxlen);
    memcpy(buffer, data.data(), len);
    return static_cast<qint64>(len);
}

QByteArray FakeReply::peek(qint64 maxlen)
{
    qDebug() << "FakeReply::peek(qint64 maxlen) got called!";
    QByteArray output;
    output.setRawData(data.data(), computeMaxNumberOfBytes(maxlen));
    return output;
}

qint64 FakeReply::size() const
{
    return data.length();
}

qint64 FakeReply::bytesAvailable() const
{
    qDebug() << "FakeReply::bytesAvailable()";
    return data.length();
}

bool FakeReply::isFinished() const
{
    qDebug() << "FakeReply::isFinished() got called!";
    return true;
}

void FakeReply::abort()
{
    qDebug() << "FakeReply::abort() got called!";
}

void FakeReply::close()
{
    qDebug() << "FakeReply::close() got called!";
}

bool FakeReply::isSequential() const
{
    qDebug() << "FakeReply::isSequential() const got called!";
    return false;
}

qint64 FakeReply::readData(char *, qint64)
{
    qDebug() << "FakeReply::readData() got called!";
    return 0;
}

the class is used like this:

QNetworkReply *MyNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
     QString path = request.url().path();
    QNetworkReply *reply = QNetworkAccessManager::createRequest(op, request, outgoingData);

     if(path == QStringLiteral("/testing")) {
         QByteArray response = waitThenReadAll(reply);
         return new FakeReply(request, response);
     }
     return reply;
   }

Solution

  • For anyone with same issue I got the answer with some code example from here: What I was missing in my code was I was using emit finish() rather

    QTimer::singleShot(0, this, SIGNAL(readyRead()));
    QTimer::singleShot(0, this, SIGNAL(finished()));
    

    That did work.