Search code examples
c++qtsecurityqnetworkreplywebsecurity

Add size limit to QNetworkReply


In my application, done with Qt5, I would like to set a security system when I download data from internet. First of all, I want to check how many bytes I am downloading and abort if it is the case. I want to set a maximum limit of downloaded bytes, and abort if it is exceeded.

I am using QNetworkReply class.

First of all I checked the size() function, but at that point, I already received all the data (the request is completed), so that is not enough.

Second check, I looked into the signal downloadProgress(qint64 bytesReceived, qint64 bytesTotal). I though, I could check that and abort if bytesReceived are too big. But I am not sure for two motivations: first, as written in the documentation

Note that the values of both bytesReceived and bytesTotal may be different from size(), the total number of bytes obtained through read() or readAll(), or the value of the header(ContentLengthHeader). The reason for that is that there may be protocol overhead or the data may be compressed during the download.

Second, I don't know when I will receive this signal....maybe it will be too late...

Finally, I checked setReadBufferSize(qint64 size). It seems a good solution, but I am not sure about the following two lines in the documentation: first,

QNetworkReply will try to stop reading from the network once this buffer is full (i.e., bytesAvailable() returns size or more)

What does it mean try? should I rely on that?

second,

Unlike QAbstractSocket::setReadBufferSize(), QNetworkReply cannot guarantee precision in the read buffer size. That is, bytesAvailable() can return more than size.

so, it seems I can not rely on a precise limit to be set.

Someone can suggest me the best way to implement it?


Solution

  • The best solution I found is to use the readyRead signal (QNetworkReply derives from QIODevice), and then implement the security check by myself in a slot:

    void onReadyRead()
    {
        for (;;)
        {
            QByteArray currentDataRead = httpReply->read(100); // Just read some bytes
            if ( currentDataRead.isEmpty() )
                return;
    
            myResponseBody.append(currentDataRead);
            if (myResponseBody.size() > myMaxResponseSize) // check my limit
            {
                qDebug() << "Error: response size bigger than expected - aborting";
                httpReply->abort();
            }
        }
    }
    

    Note: the inifite for(;;) loop and the check if ( currentDataRead.isEmpty() ) return; are needed in order to not leave some bytes out at the end, because we are inside onReadyRead that is a slot connected to readyRead and from the documentation:

    readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).

    --------------------

    PS: I am still open to better solutions, possibly native in Qt