PDA

View Full Version : Synchronous Http (Good reason)



umbrella
4th April 2008, 07:40
Hello,

First, I understand that asynchronous QHttp API is usually the right way to go. However, I have a problem that is very sequential in nature, which makes any async. solution ugly.

Briefly, the problem is as following:

Fetch document R1, parse R1, depending on what's found fetch some document R2, parse R2, depending on what's found there, fetch and parse some document R3, etc.

There are two asynchronous approaches (using QHttp) I can come up with:

(1) Create one giant state machine (keep the current state in some member variable(s) and have bunch of switch statements) - UGLY.

(2) Create a separate class for every type of document R1, R2, R3, etc. - UGLY.

A much cleaner solution would be to create a sync. way of fetching documents via Http. Then, I would not need to further complicate already complicated logic. I could also place all sync code in a separate thread to keep the QT even loop running.

QUESTION: What is the best way to implement synchronous Http functionality? Al I can think of is something along the lines of:

requestId_ = qHttp_->get("http://google.com");
while(qHttp_->hasPendingRequests()) {
qApp->processEvents();
}

plus have a slot connected to the QHttp requestFinished() signal to copy whatever data is there when the requestId_ is finished...

Any better ideas?

wysota
4th April 2008, 09:13
I'd emulate a blocking call using non blocking calls. You can even spin your own QEventLoop there if you want.

umbrella
4th April 2008, 09:25
Yes, this is what I thought... First, I tried creating and exec()-ing QEventLoop. It worked, but I did not like it, partially because I was not sure how exactly it worked and how expensive it was... The QT docs for QEventLoop are not very good I am afraid :(

I think that the solution I have right now is slightly better (it does not interfere with the main even loop), but I may be wrong. Here it is (maybe someone else will find it helpful, the timeout parameter is currently ignored):

//
// fetches the file data over http and writes it to the device; returns true if succeeds and false if an error is encountered or the http request is timed-out.
//
bool HttpFileFetcher::FetchFile(const QString* url, QIODevice* device, UINT32 timeout) {
bool rval = IS_TRUE(url != NULL && device != NULL);
if (rval) {
QUrl qUrl(*url);

rval = IS_TRUE(qUrl.isValid());
if (rval) {
setHost(qUrl.host());
requestId_ = get(*url, device);

while(currentId() != 0) {
qApp->processEvents();
}

rval = !error_;
}
}

return rval;
}

//
// called when the http request is done.
//
void HttpFileFetcher::RequestFinishedSlot(int requestId, bool error) {
if (requestId_ == requestId) {
if (error) {
error_ = error;
Log(MessageFilter::ALERT1) << L"failed to fetch an http resource: " << errorString().toAscii().data() << Log::endl;
}
}
}

//
// fetches the file data over http and writes it to the device; returns true if succeeds and false if an error is encountered or the http request is timed-out.
//
bool HttpFileFetcher::Fetch(const QString* url, QIODevice* device, UINT32 timeout) {
HttpFileFetcher fileFetcher;
return fileFetcher.FetchFile(url, device, timeout);
}

//
// fetches the file data over http and writes it to the buffer; returns true if succeeds and false if an error is encountered or the http request is timed-out.
//
bool HttpFileFetcher::Fetch(const QString* url, QByteArray* buffer, UINT32 timeout) {
bool rval = IS_TRUE(url != NULL && buffer != NULL);
if (rval) {
QBuffer device(buffer);
device.open(QIODevice::WriteOnly);

rval = IS_TRUE(device.isOpen());
if (rval) {
HttpFileFetcher fileFetcher;
rval = fileFetcher.FetchFile(url, &device, timeout);
}
}

return rval;
}

wysota
4th April 2008, 14:04
Yes, this is what I thought... First, I tried creating and exec()-ing QEventLoop. It worked, but I did not like it, partially because I was not sure how exactly it worked and how expensive it was...
It's the same loop Qt uses for its application loop. I mean similar, not the same instance.


The QT docs for QEventLoop are not very good I am afraid :(
Because it is very rarely used, so there is no point in documenting it too much.


I think that the solution I have right now is slightly better (it does not interfere with the main even loop), but I may be wrong. Here it is (maybe someone else will find it helpful, the timeout parameter is currently ignored):
It's not better. It's almost the same, just that any optimizations to the loop performed by QEventLoop (if there are any) are not used by your code.

umbrella
4th April 2008, 14:56
It's not better. It's almost the same, just that any optimizations to the loop performed by QEventLoop (if there are any) are not used by your code.[/QUOTE]

So how exactly does this work, doesn't another executing QEventLoop block the main QT event loop? Or QT manages to jump from executing one loop to another?

Thanks.

wysota
4th April 2008, 15:14
So how exactly does this work, doesn't another executing QEventLoop block the main QT event loop?
It does. That's the whole point.


Or QT manages to jump from executing one loop to another?
Look at how modal dialogs (ones triggered using QDialog::exec()) work - they use the same mechanism, they have an internal event loop.