Qt4 Synchronous HTTP Request
Problem
Creating a wrapper over QHttp that can perform GET and POST requests in a synchronous way.
Solution
In Qt4 QHttp can perform HTTP requests, but the API only allows asynchronous requests. This means that you need to specify a set of slots to handle the signals that can be emitted while the request if performed. Now don’t get me wrong, this is event driven programming and most of the times is the right way to go especially when writing a Qt based application. The cases when event driven operations prove difficult to use are rare and usually can be programmed using events.
Recently I’ve developed a small application that had to do a number of requests in a specified order and show a message if an error was to occur. The algorithm was simple:
-
-
foreach ( Request r , requestList )
-
if(! runRequest(r) ) return error;
-
For writing something like this I would like to not split the algorithm across a set of slots. So I would like to have a possibility to run a request and wait for it to finish.
Implementation
We start by creating a SyncHTTP class that is derived from QHttp:
-
-
Q_OBJECT
-
/// id of current request
-
int requestID;
-
/// error status of current request
-
bool status;
-
/// event loop used to block until request finished
-
QEventLoop loop;
-
-
public:
-
/// structors
-
-
requestID(-1),status(false){}
-
-
virtual ~SyncHTTP(){}
Now we need to implement the methods for syncGet and syncPost to mimic the get and post methods from QHttp:
-
/// send GET request and wait until finished
-
{
-
///connect the requestFinished signal to our finished slot
-
connect(this,SIGNAL(requestFinished(int,bool)),
-
SLOT(finished(int,bool)));
-
/// start the request and store the requestID
-
requestID = get(path, to );
-
/// block until the request is finished
-
loop.exec();
-
/// return the request status
-
return status;
-
}
-
/// send POST request and wait until finished
-
{
-
///connect the requestFinished signal to our finished slot
-
connect(this,SIGNAL(requestFinished(int,bool)),
-
SLOT(finished(int,bool)));
-
/// start the request and store the requestID
-
requestID = post(path, data , to );
-
/// block until the request is finished
-
loop.exec();
-
/// return the request status
-
return status;
-
}
-
{
-
/// create io device from QByteArray
-
QBuffer buffer;
-
buffer.setData(data);
-
return syncPost(path,&buffer,to);
-
}
The syncGet and syncPost functions both use the same strategy. They connect the requestFinished signal from QHttp to a finished slot, they ask the QHttp object to perform the request. The QHttp’s get/post method returns immediately with the request id that has been assigned to the current request. Now the nice part: After asking the QHttp object to perform the request we need to wait for it to finish, so we create an event loop and start it. The main event loop is now blocked and we are nor risking being called aging while executing the request. The blocking code is not using tricks like sleep or infinite loops so we don’t end up using 100% CPU or wasting time sleeping. Now we just need to implement the finished slot to save the result and exit the loop:
-
protected slots:
-
virtual void finished(int idx, bool err)
-
{
-
/// check to see if it’s the request we made
-
if(idx!=requestID) return;
-
/// set status of the request
-
status = !err;
-
/// end the loop
-
loop.exit();
-
}
-
Now this slot first checks to see if it’s being called for our request and returns if not, after that saves the error status returned by the QHttp object and exits the loop. The execution now will return in the syncGet/syncPost function that will return the status to the caller.
Testing
Ok, let’s write a small test application. Writing real automated test cases for this kind of class is possible but not trivial so will just write a small test app.
-
-
#include <QApplication>
-
#include <QDebug>
-
#include "synchttp.h"
-
-
int main(int argc, char *argv[])
-
{
-
/// create object
-
SyncHTTP h("www.google.com");
-
-
///prepare output buffer
-
QBuffer getOutput;
-
h.syncGet( "/search?q=erata.net",&getOutput );
-
qDebug() < < getOutput.data();
-
-
QBuffer postOutput;
-
-
h.syncPost( "/search",data, &postOutput);
-
-
qDebug() < < postOutput.data();
-
-
return 0;
-
}
-
Now in conclusion i would like to say that i recommend using this class only when you simply need to perform a request and know if it was successful or not. On any other case, where you need to do more complicated things learn to use Qt’s event driven programming model.
Download synchttp.h
PS: this is my first programming HOWTO so be merciful
The code and ideas from this article can be used without any license restrictions. You can consider it under BSD,MIT,BSL or whatever suites you best. Providing my name or a link to this website would be nice but is not required.

Awesome – piece of code
It is helpful – thanks a lot
Hi! I found your SyncHTTP class some months ago and have been using it since then, it’s very useful thanks!
) if that is because I changed your code or because you have modified it from the original
Today I have revisited this page to see if any updates to the code were available, and have seen the version I use is not exactly the same you have, but I don’t remember (well my memory is sooo bad
Those changes are the two methods syncSetHost and syncSetUser, and the connect() calls are moved to the constructors (to avoid re-connecting the signals in each call to this class).
So the code is as follows:
/***************************************************************************
* Copyright (C) 2005 by Iulian M *
* eti@erata.net *
***************************************************************************/
#ifndef ETKSYNCHTTP_H
#define ETKSYNCHTTP_H
#include
#include
#include
/**
* Provide a synchronous api over QHttp
* Uses a QEventLoop to block until the request is completed
* @author Iulian M
*/
class SyncHTTP: public QHttp
{
Q_OBJECT
public:
/// constructors
SyncHTTP(QObject* parent=0)
: QHttp(parent), requestID(-1), status(false)
{
///connect the requestFinished signal to our finished slot
connect(this,SIGNAL(requestFinished(int,bool)),SLOT(finished(int,bool)));
}
SyncHTTP(const QString& hostName, quint16 port=80, QObject* parent=0)
: QHttp(hostName,port,parent), requestID(-1), status(false)
{
///connect the requestFinished signal to our finished slot
connect(this,SIGNAL(requestFinished(int,bool)),SLOT(finished(int,bool)));
}
virtual ~SyncHTTP() {}
/// send GET request and wait until finished
bool syncGet(const QString& path, QIODevice* to=0)
{
/// start the request and store the requestID
requestID = get(path, to);
/// block until the request is finished
loop.exec();
/// return the request status
return status;
}
/// send POST request and wait until finished
bool syncPost(const QString& path, QIODevice* data, QIODevice* to=0)
{
/// start the request and store the requestID
requestID = post(path, data, to);
/// block until the request is finished
loop.exec();
/// return the request status
return status;
}
bool syncPost(const QString& path, const QByteArray& data, QIODevice* to=0)
{
/// create io device from QByteArray
QBuffer buffer;
buffer.setData(data);
return syncPost(path, &buffer, to);
}
bool syncSetHost(const QString& hostName, quint16 port=80)
{
/// start the request and store the requestID
requestID = setHost(hostName, port);
/// block until the request is finished
loop.exec();
/// return the request status
return status;
}
bool syncSetUser(const QString& userName, const QString& password=QString())
{
/// start the request and store the requestID
requestID = setUser(userName, password);
/// block until the request is finished
loop.exec();
/// return the request status
return status;
}
protected slots:
virtual void finished(int idx, bool err)
{
/// check to see if it’s the request we made
if (idx != requestID)
return;
/// set status of the request
status = !err;
/// end the loop
loop.exit();
}
private:
/// id of current request
int requestID;
/// error status of current request
bool status;
/// event loop used to block until request finished
QEventLoop loop;
};
#endif
goodbye!
Hi,
Glad to know this code was useful.
GREAT!!! I can do synchronous Xmlrpc calls now!!!
Thanks a lot!!!!!
Excellent piece of code!
really useful 2 years later!
best wishes for you and thank you very much
pellex
[...] few years ago I’ve written an article on how to perform a synchronous HTTP request using Qt 4.2. I I’ve seen this article today and since now I’m mostly working [...]
Thank you very much for this Excellent piece of concept.
I have question about (IHMO) mysterious line that says
‘if(idx!=requestID) return;’
How is it possible that the response could come with the Id other the was requested?
I’m also using the syncHttp code and I’ve noticed that when http request is done , it always assigns even number id’s . ‘requestFinished’ slot is fired twice, once for Id that is improper , and the second with one we want.
Can anyone explain where do the ‘bad’ reponses come from ?
@Grzegorz
It’s been a while since I’ve written this code, and at the moment i don’t have access to a Qt installation so I’m only guessing but:
I think that you can use the QHttp class to make more than one request at the same time, each having it’s own request id and that’s probably why trolltech designed the interface this way.
As for the “bad” responses i remember that i was curious about this at the time and that i discovered that the QHttp class can make other internal, protocol specific, requests witch are handled the same way as normal requests. I think that braking with a debugger when the “bad” response arrives will give you a clearer answer.