How to post QHttpPart with sequential QIODevice
-
I am writing an application that is posting data as http multipart to a server. One of the parts is ordinary text, the other part is read from a sequential QIODevice.
Post with QNAM hangs after all of the data is read from the device.
There is a note in the "documentation":http://qt-project.org/doc/qt-4.8/qhttppart.html that:
bq. if device is sequential (e.g. sockets, but not files), QNetworkAccessManager::post() should be called after device has emitted finished().
However "QIODevice":http://qt-project.org/doc/qt-4.8/qiodevice.html doesn't emit finished() at all. Is there an error in the documentation?
I tried to use my custom sequential QIODevice as I would use a file. All data was read from the device after QNAM::post was called, but then sending hanged. Without calling post, reading didn't start at all.
This looks like catch 22: post() expects finished() to be emitted, but no data is read before post() is called...
What is the proper way to use sequential QIODevice with QHttpMultipart?
@QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart metaPart;
metaPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
metaPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name="metadata""));
metaPart.setBody(meta.toJson());
multiPart->append(metaPart);QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(fileFormat));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name="file""));
filePart.setBodyDevice(p_encDevice);
p_encDevice->setParent(multiPart); // we cannot delete the device now, so delete it with the multiPart
multiPart->append(filePart);QNetworkAccessManager netMgr;
QScopedPointer<QNetworkReply> reply( netMgr.post(request, multiPart) );
multiPart->setParent(reply.data()); // delete the multiPart with the reply
@ -
Qt5 Documentation for "QHttpPart::setBodyDevice":http://qt-project.org/doc/qt-5.0/qtnetwork/qhttppart.html#setBodyDevice contains the same instructions.
Can anyone with experience using sequential devices in QHttpMultipart provide me insights, please?
-
I asked the same question on stackoverflow, but I didn't receive any useful answer:
-
As far as I can see the QHttpMultiPart and QNetworkReply... try to read until the device returns -1 when calling read(...)
Make sure that your implementation of readData(...) returns -1 when it gets called after all data was already read!... as QHttpMultiPart doesn't listen to any signales, the signals of your device don't care, it's all about the return-code of read/readData
-
Thanks for tips. My code returns -1 when all data is read. I am posting the trace from my class after QNAM::post() is called.
As you can see there is a read request for more data than actually available. This is the only call to readData of my class. Next call would return -1, however this call doesn't happen.
@
[13-03-12 13:51:56.091] isSequential
[13-03-12 13:51:56.092] isSequential
[13-03-12 13:51:56.100] size: 70925
[13-03-12 13:51:56.101] isSequential
[13-03-12 13:51:56.121] size: 70925
[13-03-12 13:51:56.122] isSequentialreadData -->
[13-03-12 13:51:56.157] Read request: 70972
[13-03-12 13:51:56.158] Read from source: 70917
[13-03-12 13:51:56.160] Reached source EOF.
[13-03-12 13:51:56.161] Completing encryption of message.
[13-03-12 13:51:56.163] Return: 70925 , remains 0
<-- readData[13-03-12 13:51:56.163] size: 70925
[13-03-12 13:51:56.164] size: 70925
[13-03-12 13:51:56.165] size: 70925-- everything stops here --
@ -
I first was a bit puzzled how situation is exactly ... so I setup a small project to reproduce it and I also get to the point that it reads (on my case it even reads with -1 twice) ...
As I was now confused what all the "isSequential" special handling should do, I changed it to return false (even so my device acts like sequential) ... and then it worked, it generated the data and send it to my webserver ... not sure what the side-effect of this is
-
Btw. I think the code which queries isSequential() interprets it as "device doesn't know his size" ... and the QNAM code that follows on isSequential==true buffers all data from the device (probably to calculate a valid content-length) ...
I haven't saw any code that use the other aspects of "isSequential" (like seek() or pos()) ... so if you could calculate the size and don't want the data to be buffered, isSequential==false and a valid size seems to be the right way
... but that still doesn't explain why QHttpMultiPart with isSequential doesn't work ... looking into unit-tests ... btw. THERE IS NO UNIT TEST for isSequential-Devices in the unit-tests for QHttpPart, so if this code is broken it wouldn't be detected anyway ...
-
Thanks. I was reviewing the Qt code as well. Your post confirms what I suspect.