QNetworkReply and QNetworkAccessManager timeout in http request - c++

How do I setup a timeout when I do an http request?
I have this code:
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.foo.com"));
request.setRawHeader("User-Agent", USER_AGENT.toUtf8());
request.setRawHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
request.setRawHeader("Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language", "en-us,en;q=0.5");
request.setRawHeader("Connection", "Keep-Alive");
reply = m_networkManager->get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
}
Where and how do I setup some kind of timeout to the request?

QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
timer.start(30000); // 30 secs. timeout
loop.exec();
if(timer.isActive()) {
timer.stop();
if(m_reply->error() > 0) {
... // handle error
}
else {
int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (v >= 200 && v < 300) { // Success
...
}
}
} else {
// timeout
disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
reply->abort();
}

check this out:
https://doc.qt.io/qt-5/qnetworkrequest.html#setTransferTimeout
void QNetworkRequest::setTransferTimeout(int timeout =
DefaultTransferTimeoutConstant) Sets timeout as the transfer timeout
in milliseconds.
Transfers are aborted if no bytes are transferred before the timeout
expires. Zero means no timer is set. If no argument is provided, the
timeout is QNetworkRequest::DefaultTransferTimeoutConstant. If this
function is not called, the timeout is disabled and has the value
zero.
This function was introduced in Qt 5.15.

Related

Qt Stop worker thread loop from GUI Thread

i have a worker thread which i try to stop through a button. i sometimes get the following error:
Fatal: QThread: Destroyed while thread is still running
Here is my code. By pushing the disconnect button a signal is emitted to stop the thread's while loop.
if (ui->connectButton->text() == "Connect") {
mUDPThread = new QThread;
mUDPWorker = new UDPThread(ui->HostTextEdit->toPlainText(), ui->portTextEdit->toPlainText().toInt());
mUDPWorker->moveToThread(mUDPThread);
connect(mUDPThread, SIGNAL(started()), mUDPWorker, SLOT(process()));
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(quit()));
connect(mUDPWorker, SIGNAL(finished()), mUDPWorker, SLOT(deleteLater()));
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
connect(this, SIGNAL(onExitThread()), mUDPWorker, SLOT(onExitThread()));
connect(this, SIGNAL(onDataIncome(QString)), mUDPWorker, SLOT(onDataIncome(QString)));
mUDPThread->start();
ui->connectButton->setText("Disconnect");
} else if (ui->connectButton->text() == "Disconnect") {
emit onExitThread();
ui->connectButton->setText("Connect");
}
the worker Thread:
void UDPThread::process() {
while (isRunning) {
QCoreApplication::processEvents();
...
}
emit finished();
}
void UDPThread::onExitThread() {
qDebug() << "onExitThread" << isRunning;
isRunning = false;
}
while (isRunning)
You are blocking the thread right there, its event loop cannot spin in order to receive signals, it can only send signals to other threads with spinning event loops.
You need to make your worker non-blocking, split the work in cycles, in between the event loop gets to spin and receive signals. In pseudocode:
if (isRunning) {
doWorkCycle();
scheduleNextWorkCycle();
} else emit finished();
There is an example you can look at here.
Solved it. The error was within the connect commands:
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
to
connect(mUDPThread, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
result:
connect(mUDPThread, SIGNAL(started()), mUDPWorker, SLOT(process()));
connect(mUDPWorker, SIGNAL(finished()), mUDPThread, SLOT(quit()));
connect(mUDPWorker, SIGNAL(finished()), mUDPWorker, SLOT(deleteLater()));
connect(mUDPThread, SIGNAL(finished()), mUDPThread, SLOT(deleteLater()));
connect(this, SIGNAL(onExitThread()), mUDPWorker, SLOT(onExitThread()));
connect(this, SIGNAL(onDataIncome(QString)), mUDPWorker, SLOT(onDataIncome(QString)));

QNetworkReply::NetworkError(ProtocolUnknownError)

In my project, I am attempting to download a YouTube profile picture. The YouTube API returns this link https://yt3.ggpht.com/-7ipuUvDjVT8/AAAAAAAAAAI/AAAAAAAAAAA/hSPOcUsb1nw/s240-c-k-no-mo-rj-c0xffffff/photo.jpg
However, using the same function for downloading files from my own website doesn't work with this URL. I get QNetworkReply::NetworkError(ProtocolUnknownError) in the application output.
Here is my code:
QByteArray downloadFileData(QString url)
{
QNetworkRequest request;
request.setUrl(QUrl(url));
QNetworkReply *reply = manager->get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
if(reply->error() == QNetworkReply::NoError)
{
return reply->readAll();
}
else
{
qDebug() << reply->error();
}
reply->deleteLater();
return "";
}
When I pass that url to this function, I get that error. What is going on?
Thanks for your time

Getting a page content with Qt

I am trying to get the content of a HTTP request into a QString variable with Qt and C++
QNetworkAccessManager networkManager;
QUrl url("https://someurl.test.com/this-actually-exists");
QNetworkRequest request;
request.setUrl(url);
QNetworkReply* currentReply = networkManager.get(request); // GET
QString reply = QTextCodec::codecForMib(1015)->toUnicode(currentReply->readAll());
Still, the variable reply seems to stay empty. Obviously, I misunderstand the documentation. How do I get this to perform?
You can use two different ways even the synchronous or asynchronous ways to do this. The asynchronous way is :
connect (&networkManager , SIGNAL(finished(QNetworkReply*)) ,this, SLOT(done(QNetworkReply*)));
networkManager.get(request);
And you should read the contents from the returned reply in the slot connected to finished signal in the following way :
void net::done(QNetworkReply * reply)
{
if (reply->error() == QNetworkReply::NoError)
{
data = QString(reply->readAll ());
}
else
{
data = QString(reply->errorString ());
}
}
The synchronous way is like :
QNetworkReply *reply = networkManager.get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
loop.exec();
QByteArray bts = reply->readAll();
QString str(bts);
Here you use an event loop to wait until the reply is finished and then read the available bytes and get the string.
I need to assume you're running an application with an event-loop in place? If not, then it's a bit harder...
If so, replace your last line that builds the reply QString:
connect(currentReply, SIGNAL(finished()), this, SLOT(gotAReply()));
Then you'll have to define another method in your class as a slot that gets triggered as soon as that reply got filled:
void gotAReply()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
if (reply)
{
if (reply->error() == QNetworkReply::NoError)
{
QString replyText( reply->readAll() );
}
reply->deleteLater();
}
}
Don't forget: for Signals and Slot to work your class declaration must contain the Q_OBJECT macro.

QWebView Wait for load

bool MainWindow::waitForLoad(QWebView& view)
{
QEventLoop loopLoad;
QTimer timer;
QObject::connect(&view, SIGNAL(loadFinished(bool)), &loopLoad, SLOT(quit()));
QObject::connect(&timer, SIGNAL(timeout()), &loopLoad, SLOT(quit()));
timer.start(timeout);
loopLoad.exec();
if(!timer.isActive())
{
timer.stop();
view.stop();
return false;
}
return true;
}
Say me, is that a correct code? App sometimes freezes after line
loopLoad.exec();
And always returns true even here has happened some problem (timeout, errors when loading, ect -- always true).
start(timeout); starts the timer with a timeout interval of msec milliseconds. So after calling it the timer is running and timer.isActive() always returns true and the if block does not get executed.
You should stop the timer when loadFinished is emitted :
QObject::connect(&view, SIGNAL(loadFinished(bool)), &timer, SLOT(stop()));
If the timer is active then the event loop is stopped by the timer so you should return false because a timeout has been occurred. You should replace if(!timer.isActive()) with if(timer.isActive()).
The correct code is :
bool MainWindow::waitForLoad(QWebView& view)
{
QEventLoop loopLoad;
QTimer timer;
QObject::connect(&view, SIGNAL(loadFinished(bool)), &loopLoad, SLOT(quit()));
QObject::connect(&view, SIGNAL(loadFinished(bool)), &timer, SLOT(stop()));
QObject::connect(&timer, SIGNAL(timeout()), &loopLoad, SLOT(quit()));
timer.start(timeout);
loopLoad.exec();
if(timer.isActive())
{
timer.stop();
view.stop();
return false;
}
return true;
}

QNetworkAccessManager issue

QString My_class::My_Method()
{
QNetworkAccessManager *manager= new QNetworkAccessManager(this);
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(ReplayFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl(My_URL)));
return str;
}
void My_class::ReplayFinished(QNetworkReply *replay)
{
QString buffer;
if(replay->isOpen())
{
buffer=replay->readAll();
//treatment on the buffer and the public Qstring 'str'(declared in My_class.h) is updated
}
}
Hi,
the problem is that when calling the My_method() in the main, the QString str is empty because it was returned that way without waiting for the ReplayFinished to update it.
What could be the solution to wait until the job is complete so I can get the proper information, not skipping it and returning something else.
Thank you.
You can use an event loop to wait until the reply is finished and then read the available bytes and return the string :
QString My_class::My_Method()
{
QNetworkAccessManager manager;
QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(My_URL)));
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
loop.exec();
QByteArray bts = reply->readAll();
QString str(bts);
delete reply;
return str;
}