Read http request-headers (Qt/c++) - c++

I'm trying to read http request-headers that I can log into the log file (using Qt/c++). I'm able to read the response headers using following simple code:
QList<QByteArray> headerList = pReply->rawHeaderList();
foreach(QByteArray head, headerList)
{
qDebug() << head << ":" << pReply->rawHeader(head);
}
pReply->close();
But so far I had no luck with request headers. While looking for the solution I came across
this post: Read complete HTTP request-header; But I didn't really understand how to achieve similar functionality with Qt.
I'm bit lost. How should I go about this?

The rawHeader is actually a QPair of QByteArray. See: RawHeader. You either do a for each with the RawHeader instead of QByteArray or just iterate through the list:
QList<QByteArray> headerList = pReply->rawHeaderList();
for (int i = 0; i < rawHeaderList.count(); ++i) {
qDebug() << head << ":" << pReply->rawHeader(i);
}
pReply->close();

There is no direct method to get headers of request, but you can get header list and iterate over them and save in a QVariantMap. here is a sample code.
auto reqHeaderName = reply->request().rawHeaderList();
QVariantMap reqHeaders;
for (QString header : reqHeaderName)
{
reqHeaders.insert(header, reply->request().rawHeader(header.toUtf8()));
}

Related

Converting JSON array to QByteArray

I have an array: [0xa,0x0b,0x0c]
This is stored in QJsonArray, I want to cover this to a QByteArray. I've been searching around for a solution and have come across several methods, this is what I have tried but its not right:
QJsonObject::iterator itrBinary = objJSON.find(clsFileThread::mscszBinary);
if ( itrBinary != objJSON.end() ) {
QJsonArray aryBinary(itrBinary->toArray());
//At this point aryBinary contains:
//10,11,12 which is correct
QJsonDocument doc(aryBinary);
QByteArray aryBytes(doc.toBinaryData());
//Now aryBytes contains:
//'q','b','j' why, how?
qDebug() << aryBinary << aryBytes;
}
After the qDebug I get:
QJsonArray([10,11,12]) "qbjs\x01\x00\x00\x00\x18\x00\x00\x00\x06\x00\x00\x00\f\x00\x00\x00J\x01\x00\x00j\x01\x00\x00\x8A\x01\x00\x00"
What I want in QBytesArray is exactly what was put into the QJsonArray, 10, 11, 12.
Thank you to "eyllanesc" for input, I would have thought there would be a built in function to do this, but here is the solution:
QJsonArray aryBinary(itrBinary->toArray());
QJsonArray::iterator itrArray = aryBinary.begin();
QByteArray aryBytes;
while( itrArray != aryBinary.end() ) {
aryBytes.append(static_cast<char>(itrArray->toInt()));
itrArray++;
}
qDebug() << aryBinary << aryBytes;

ZeroMQ sockets block

I just can't understand what i did. How it works? It works just by half, but there is identical pieces of code(almost).
I have client-server application. It sends any requests, and getting response, either list, that i will transform to vector, or string that contains HTML code. So, I will try to explain, but you are welcome to ask as much questions as you want.
1) I request file from server, by sending command "2" and path. Here is code
void Connection::requestFile(string path)
{
//string cookedPath = "./" + path;
string reply = this->sendCommand("2\n" + path);
vector<string> response = this->divideString(reply);
// set as files list we got. First is a helper, so we will not add it to files list.
if (response[0] == "directories") {
// remove "directories" entry so it will not be listed then
response.erase(response.begin());
this->files = response;
this->displayHtml = false;
} else {
//else server sent string with html
this->html = reply;
this->displayHtml = true;
}
}
displayHtml here is kinda switch that will help to determine what to do.
So in this method i've used sendCommand() method, which by my opinion is origin of all troubles.
Here it comes
string Connection::sendCommand(std::string command)
{
// send command
zmq::message_t request(command.length());
memcpy (request.data(), command.c_str(), command.length());
Connection::socket.send(request);
// get reply
zmq::message_t reply;
Connection::socket.recv(&reply);
// make string out of reply
std::string rpl = std::string(static_cast<char*>(reply.data()), reply.size());
return rpl;
}
Sorry about this formatting.
Then i use these methods here
void MainWindow::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{
// set our current folder to ./folder/ + selected file
connection->currentPath = connection->currentPath + item->text().toStdString();
// update file list. as argument we give path we just got
// std::cout << connection->currentPath << endl;
ui->listWidget->clear();
connection->requestFile(connection->currentPath);
if (connection->displayHtml == true) {
// webview->updateHtml(connection->html);
cout << "html";
} else {
this->updateFilesList(connection->files);
cout << "fileslist";
}
}
This method will be called when i double click on item in Qt widget list.
Say i've double clicked on item. It partially works fine, but it isnt prints it.
But if i close application, it prints.
So i guess problem is in blocking on 'recv' zmq function, but why it works for a half then? Maybe rewrite it anyhow? Thanks.

Creating a QVariantMap in Qt with strings

Apologies for asking something this trivial but I just can't seem to get it right so I guess I've completely misunderstood everything I thought I knew about memory management.
I have a function that parses a network reply for some data and it looks like this:
// Call from another function
QVariantMap *mappedResult = handleReply(reply);
...
// Function
QVariantMap *handleReply(QNetworkReply *reply) {
QVariantMap *result = new QVariantMap;
QVariant testvalue = new QVariant("testvalue");
result->insert("testkey", testvalue);
if (reply->error() > 0) {
qDebug() << "Error number = " + reply->errorString();
QVariant variant = QVariant.fromValue(reply->errorString());
result->insert("error", variant);
} else {
QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonString.toUtf8());
QJsonObject jsonResponseObject = jsonResponse.object();
*result = jsonResponseObject.toVariantMap();
}
return result;
}
If there is no error, the result is parsed fine by the built in toVariantMap function. When there is an error however, I would like to create an entry in the result that is sent back from the function. As you can see in the function, I'm trying to create QVariants from Strings in two different ways.
The first approach gives an error like this:
C:\Qt\5.2.0\mingw48_32\include\QtCore\qvariant.h:466: error: 'QVariant::QVariant(void*)' is private inline QVariant(void *) Q_DECL_EQ_DELETE;
^
The second approach like this:
[PATH] error: expected primary-expression before '.' token QVariant variant = QVariant.fromValue(reply->errorString());
I've also tried setting the values like this:
result->insert("testkey", "testvalue");
result->insert("error", reply->errorString());
The last approach doesn't give compile errors but the result variable in the return statement cannot be inspected in the debugger and as soon as the return statement is executed the application crashes with memory problems indicating I'm not using "new" properly.
"Error accessing memory address 0x...".
If someone with at least a little knowledge could help me sort out how to perform this simple task, I would be very greatful. How can I return a QVariantMap from my function with custom strings as key/value pairs?
Cheers.
I would write your function in the following way (with fixes):
QVariantMap *handleReply(QNetworkReply *reply) {
QVariantMap *result = new QVariantMap;
QVariant testvalue("testvalue"); // <- fixed here
result->insert("testkey", testvalue);
if (reply->error() > 0) {
qDebug() << "Error number = " + reply->errorString();
QVariant variant(reply->errorString()); // <- fixed here
result->insert("error", variant);
} else {
QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonString.toUtf8());
QJsonObject jsonResponseObject = jsonResponse.object();
*result = jsonResponseObject.toVariantMap();
}
return result;
}
However it is still unclear, why do you need to work with a pointer to the variant map instread of passing it by value?

Save QNetworkReply

I would like to be able to save the QNetworkReply to a QString/QByteArray. In the examples I've seen they always saves the stream to another file.
At the moment my code looks something like this, where I get a string from the host and all I want to do is to parse it to look for the specified error code.
if(_reply->error() == QNetworkReply::UnknownContentError) {
qDebug() << _reply->readAll(); // prints out the xml message
QString test = QString(_reply->readAll());
qDebug() << test; // ""
QByteArray test2 = QByteArray(_reply->readAll());
qDebug() << test2; // ""
QRegExp rxlen("(<code>)(.*(?=</code>))");
rxlen.setMinimal(true);
int pos = rxlen.indexIn(test); // pos == -1
if(pos > -1) {
qDebug() << rxlen.cap(2); // never hit
}
}
The message is pretty small and looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<error>
<code>string-value</code>
<message>string-value</message>
</error>
So how can I load this small stream into memory, or just look for the error code?
QNetworkReply inherits from QIODevice which is a stream. After you have read something from a stream it's not there anymore. After your debug line (one with // prints out the xml message comment) there's nothing to read anymore.

How do I read headers from a QNetworkReply

How can one read headers for example a cookie out of a QNetworkReply?
I just thought to add to the above answer concerning rawHeader
QList<QByteArray> headerList = reply->rawHeaderList();
foreach(QByteArray head, headerList) {
qDebug() << head << ":" << reply->rawHeader(head);
}
Consulting the documentation, there are a few methods related to reading headers: header, rawHeader, rawHeaderList, and rawHeaderPairs. For the specific case of getting a cookie, you can use the header method. It would look something like this:
QNetworkReply *reply;
// somehow give reply a value
QVariant cookieVar = reply.header(QNetworkRequest::CookieHeader);
if (cookieVar.isValid()) {
QList<QNetworkCookie> cookies = cookieVar.value<QList<QNetworkCookie> >();
foreach (QNetworkCookie cookie, cookies) {
// do whatever you want here
}
}
The header method only works for certain HTTP headers, though. In the general case, if there is no QNetworkRequest::KnownHeaders value for the header you want, the rawHeader method is probably the way to go.
I have tried the answer of Evan Shaw, but there is a little mistake.
The QNetworkRequest::CookieHeader need change to QNetworkRequest::SetCookieHeader. Because I found it is Set-Cookie in the header of QNetworkReply other than Cookie.
QNetworkReply *reply;
// somehow give reply a value
QVariant cookieVar = reply.header(QNetworkRequest::SetCookieHeader);
if (cookieVar.isValid()) {
QList<QNetworkCookie> cookies = cookieVar.value<QList<QNetworkCookie> >();
foreach (QNetworkCookie cookie, cookies) {
// do whatever you want here
}
}