qnetworkreply no statuscode or error but failing - c++

Im trying to find out what goes wrong when doing a get request in qt.
I have the following slots attached to my networkmanager:
connect(mgr,SIGNAL(finished(QNetworkReply*)),this,SLOT(requestFinished(QNetworkReply*)));
connect(mgr, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError(QNetworkReply::NetworkError)));
The request finished is like:
void FirebaseInteractor::requestFinished(QNetworkReply *rep)
{
QVariant statusCode = rep->attribute( QNetworkRequest::HttpStatusCodeAttribute );
int status = statusCode.toInt();
if ( status != 200 )
{
QString reason = rep->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << "Pushnotification Request failed : " <<reason;
}
else{
qDebug() << "Pushnotification has been send: ";
}
if ( !statusCode.isValid() )
{
QString status = statusCode.toString(); // or status_code.toInt();
qDebug() << "Failing " << status;
int code = statusCode.toInt();
qDebug() << "Pushnotification Request failed invalid status code." << QString::number(code);
QString reason = rep->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << "reason " << reason;
return;
}
}
However Status is always empty Failing and reason is printed but there is no value after it (i was expecting a reason e.g. timeout, 401 etc).
I also tried:
int status = statusCode.toInt();
if ( status != 200 )
{
QString reason = rep->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString();
qDebug() << "Pushnotification Request failed : " <<reason;
}
else{
qDebug() << "Pushnotification has been send: ";
}
But all reasons/codes are empty.
I also added:
void FirebaseInteractor::slotError(QNetworkReply::NetworkError error)
{
qDebug() << "slotError" << error;
}
but this is not called.
How can I find out whats going wrong?

You can get the error directly using:
qDebug() << reply->error();
This won't work if the network request never happened:
if ( !statusCode.isValid() )
Because this means that the QVariant itself is invalid and has type QMetaType::UnknownType Documentation. Hence it will not give any information about what went wrong in the http request. To fix this, here's a simple example:
if (statusCode.isValid()) { // it needs to be valid
qDebug() << "Status Code: " << statusCode.toString();
}
Alternatively, you can switch on QNetworkReply::Error()
void FirebaseInteractor::requestFinished(QNetworkReply *rep)
{
switch (rep->error())
{
case QNetworkReply::NoError:
// No error
return;
case QNetworkReply::TimeoutError:
{
auto httpStatus =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
auto httpStatusMessage = reply->attribute(
QNetworkRequest::HttpReasonPhraseAttribute).toByteArray();
//...
break;
}
}
}

Related

QNetworkRequest causes memory corruption

I created a library which will handle all HTTP requests and parsing of response data in JSON format. When I called the method that includes get request in my main application (with GUI), I received a memory corruption error. So I added QEventLoop and a timer to wait for the response before proceeding to other processes. I was able to get the response data by calling QNetworkReply.readall(). I needed to get the char* value of the response data so I called the QNetworkReply.data() but it is empty. Why?
Here are the codes I wrote:
Library which handles HTTP requests:
void HttpRequest::getRequest(string param1, string param2)
{
pManager_ = new QNetworkAccessManager(this);
QUrl cUrl(sampleUrl);
QNetworkRequest request(cUrl);
request.setRawHeader(keyHeader.c_str(), param1.c_str());
connect(pManager_, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
connect(pManager_, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )), this,
SLOT(handleSslErrors(QNetworkReply*, const QList<QSslError> & )));
cUrl.addQueryItem("name", QString::fromStdString(param2));
pManager_->get(request); // memory corruption error encountered in main application after calling this
std::cout << "after calling get" << std::endl;
}
void HttpRequest::requestFinished(QNetworkReply *pReply)
{
QByteArray responseData;
std::cout << " request finished" << std::endl;
int responseStatus = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
std::cout << " status code: " << responseStatus << std::endl;
if(pReply->error())
std::cout << " Error: " << pReply->errorString().toStdString() << std::endl;
else
{
responseData = pReply->readAll();
qDebug() << " Response data: " << responseData;
const char* pResponseData = responseData.data();
qDebug() << "pResponseData: " << pResponseData ;
// parsing here
}
pReply->deleteLater();
pManager_->deleteLater();
}
void HttpRequest::handleSslErrors(QNetworkReply *pReply, const QList<QSslError> & )
{
std::cout << " SSL ERROR" << std::endl;
int responseStatus = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
}
Main GUI application:
DialogTest::DialogTest(QWidget *parent) :
QDialog(parent),
ui(new Ui::DialogTest)
{
// some codes here
if(enabled)
{
HttpRequest::instance()->getInformation(param1, param2); // memory corruption happened here when I called getRequest() method with no event loop
}
// other threads here
}
Here is the code that uses QEventLoop:
void HttpRequest::getRequest(string param1, string param2)
{
QTimer qTimer;
QEventLoop loop;
pManager_ = new QNetworkAccessManager(this);
QUrl cUrl(sampleUrl);
QNetworkRequest request(cUrl);
request.setRawHeader(keyHeader.c_str(), param1.c_str());
connect(&qTimer,SIGNAL(timeout()),&loop, SLOT(quit()));
connect(pManager_, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit()));
QNetworkReply *pReply = pManager_->get(request);
qTimer.start(1000);
loop.exec();
int responseCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
std::cout << "status code: " << responseCode << std::endl;
if(pReply->error())
{
std::cout << " Error: " << pReply->errorString().toStdString() << std::endl;
}
else
{
qDebug() << "[HttpRequest] Response data: " << pReply->readAll();
QByteArray response = pReply->readAll(); // it printed this value: "{"count":3,"codes":["x00000A","x00000B","x00000C"]}" which is correct
char* pResponseData = response.data();
qDebug() << "pResponseData: " << pResponseData ; //it printed this: pResponseData:
}
delete pReply;
delete pManager_;
}
I am expecting this response data from a HTTP get command:
"{"count":3,"codes":["x00000A","x00000B","x00000C"]}"
Problem:
What is the best way to implement this? I want to put all HTTP request in a library then call it my main application with GUI. Please note that:
When I use QEventLoop inside the library to wait for the response, QNetworkReply.data() is empty. I need the value of QNetworkReply.data() for parsing.
When I did not use QEventLoop and use signal and slot alone (as shown in the code above), memory corruption occurred in main application after executing HTTP get command. No response data is received.
an advice:
never use a direct delete for a QObject. BAD:
delete pReply;
delete pManager_;
Qt way,GOOD:
pReply->deleteLater();
pManager->deleteLater();
Better: no "new" (dynamic memory)
QNetworkAccessManager Manager_;
...
connect(&Manager_, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit()));
..
pReply->deleteLater();

POCO websocket messes after 50 minutes of working

I have the following question related to POCO library. My client listens the messages from our backend server using POCO library. All is well the first 50 minutes, then with the socket happens something strange and method "receiveFrame" begins to return an exception. After that, the socket does not become operational. I have made few tests the time after which I receive not operational socket is exactly 50 minutes. Also I need to note that our backend server doesn't send anything during all time. I have no idea what happens... Below is code of our Handshake and Read procedures:
void WebSocketManager::Handshake()
{
qDebug() << "WebSocketManager::Handshake";
try {
HTTPResponse response;
QString origin = Settings::Instance()->GetErPortal();
QString host = origin.remove("http://");
host = host.remove('/');
QString token = "/event/bus/ws/subscribe?auth_token=" + Settings::Instance()->token().toUtf8();
_wssession.setHost(host.toUtf8().constData());
_wssession.setPort(80);
HTTPRequest request(HTTPRequest::HTTP_GET, token.toUtf8().constData(),HTTPMessage::HTTP_1_1);
request.set("origin", origin.toUtf8().constData());
_wssock = new WebSocket(_wssession, request, response);
response.getStatus();
HTTPResponse::HTTPStatus status = response.getStatus();
qDebug() << "Handshake status is : " << status;
if(status == HTTPResponse::HTTPStatus::HTTP_SWITCHING_PROTOCOLS)
_status = true;
}
catch (std::exception &e)
{
qDebug() << "WebSocketManager::Handshake exception " << e.what();
}
}
void WebSocketManager::Read()
{
char receiveBuff[1024];
while(_status)
{
qDebug() << "WebSocketManager::Read wait data...., thread = " << QThread::currentThread();
try {
int flags=0;
int rlen=_wssock->receiveFrame(receiveBuff,1024,flags);
if(!rlen)
{
qDebug() << "WebSocketManager::Read error";
emit ConnectionFailed();
return;
}
else
{
qDebug() << "WebSocketManager::Read, len =" << rlen << ", flags = " << flags << ", data = " << receiveBuff;
ProcessBackendEvent(QString(receiveBuff));
}
}
catch (std::exception &e)
{
qDebug() << "WebSocketManager::Read exception " << e.what();
}
}
}
It seems it is bug of POCO library described here https://github.com/pocoproject/poco/issues/490
On 1.9.0 POCO library all work fine...

Modify "Modbus TCP" to "Modbus RTU" with QT - QModbusTcpClient

I would have to send Modbus request for specific data, my problem is that I have to use mobile communication, wifi, connect to a custom electronic card, which is right in Modbus RTU.
My working code connecting to the electronic board:
#include "connessione.h"
#include <QModbusTcpClient>
#include <QVariant>
#include <QModbusDataUnit>
#include <QDebug>
connessione::connessione(QObject *parent) : QObject(parent)
{
qDebug() << "here debug " << "ok";
clientX = new QModbusTcpClient();
clientX->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "192.168.222.1");
clientX->setConnectionParameter(QModbusDevice::NetworkPortParameter, 5555);
if (clientX->connectDevice())
{
qDebug() << "connected: " << clientX->state();
}
else
{
qDebug() << "ERRORE" << clientX->errorString();
}
}
void connessione::clickButton(){
QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 1); // just read input register 40006
//qDebug() << "readUnit" << readUnit.RegisterType;
qDebug() << "readUnit" << clientX->state();
if (auto *reply = clientX->sendReadRequest(readUnit, 255)) // client id 255
{
if (!reply->isFinished())
{
// connect the finished signal of the request to your read slot
qDebug() << "connected" << reply->errorString();
connect(reply, &QModbusReply::finished, this, &connessione::readReady);
}
else
{
qDebug() << "Errore" << reply->errorString();
delete reply; // broadcast replies return immediately
}
}
else
{
qDebug() << "Errore" << reply->errorString();
// request error
}
}
void connessione::readReady()
{
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
if (reply->error() == QModbusDevice::NoError)
{
const QModbusDataUnit unit = reply->result();
int startAddress = unit.startAddress(); // the start address,
int value = unit.value(0); // value of the start address + 0
qDebug() << "NESSUN ERRORE" << reply->errorString();
}
else
{
qDebug() << "Errore readReady" << reply->errorString();
// reply error
}
reply->deleteLater(); // delete the reply
}
log string TCP sent:
D/libmodbusMobile.so( 8042): (null):0 ((null)): qt.modbus: (TCP
client) Sent TCP PDU: 0x0300000001 with tId: 2
this is right: 0x0300000001
But unfortunately, my electronic card, the integrated firmware I can not modify, is right with Modbus RTU, so I should change 0x0300000001 to 0x010300000001C1C2 where C1 and C2 are the checksums.
I believe that QModbusDataUnit generate buffer to send. So how to change it? Exist manual solution where I build the buffer?
how to change it and create custom send buffer like the example?
Thanks

CPPRest SDK making HTTP request to Server

during CPPRest SDK (2.8) testing, I initialized an HTTP Request simulating user login to the local server, I am expecting a JSON string to be returned indicating if login succeed. here is the code I wrote.
void printJSON(json::value v)
{
if (!v.is_null()){
// Loop over each element in the object
for (auto iter = v.as_object().cbegin(); iter != v.as_object().cend(); ++iter){
const string &key = iter->first;
const json::value &value = iter->second;
if (value.is_object() || value.is_array()){
if(key.size() != 0){
std::wcout << "Parent: " << key.c_str() << std::endl;
}
printJSON(value);
if(key.size() != 0){
std::wcout << "End of Parent: " << key.c_str() << std::endl;
}
}else{
std::wcout << "Key: " << key.c_str() << ", Value: " << value.to_string().c_str() << std::endl;
}
}
}
}
void login(){
http_client client("http://localhost:8080/user");
http_request request(methods::POST);
request.headers().add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
request.headers().add("Content-Length", "100");
request.headers().add("Host", "testhost.com");
request.headers().add("X-Requested-With", "XMLHttpRequest");
request.set_body("u_id=test_admin&pwd=123456789");
pplx::task<void> task = client.request(request)
.then([](http_response response)-> pplx::task<json::value>{
if(response.status_code() == status_codes::OK){
return response.extract_json();
} else {
return pplx::task_from_result(json::value());
};})
.then([](pplx::task<json::value> previousTask){
try{
const json::value & v = previousTask.get();
printJSON(v);
} catch(const http_exception &e){
std::cout<<e.what()<<std::endl;
}
});
try{
task.wait();
} catch(std::exception &e){
std::cout<<e.what()<<std::endl;
}
}
When I run this code, nothing happened, it seems request never reaches to the Server which has been tested using JSP, So I am pretty sure something went wrong in my code. please help, thanks
Vague.
If you're saying that the request is not reaching the server, there might be a problem with the listener at the time you executed this code.
Your request format is correct and running, you may try to wrap the body (u_id, pwd) into a json, and see if it works.
The bottomline is debugging or sharing your server code would probably help clarify things a bit more.

QWebPage not working after the first load

I'm writing an application that scrapes some web pages using QWebPage. I'm having some trouble when the response is a Http redirect (e.g 302, 303, etc). The QWebPage simply does not follow the redirect.
To work around this issue I've connected to the page's network manager's finished signal to capture the status of the response and load any redirect, however, when I call the load method for the second time on the QWebPage, it justs sets the url to blank and doesn't issue any request whatsoever.
Here are some relevant bits of code:
connect(page->networkAccessManager(), SIGNAL(finished(QNetworkReply*)), SLOT(gotReply(QNetworkReply*)));
connect(page, SIGNAL(loadFinished(bool)), SLOT(doneLoading(bool)));
page->mainFrame()->load(url);
My slot:
void Snapshot::gotReply(QNetworkReply *reply)
{
if(reply->header(QNetworkRequest::ContentTypeHeader).toString().contains(QString("text/html")))
{
qDebug() << "Got reply " + reply->url().toString() + " - " + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString() + " - " + reply->header(QNetworkRequest::ContentTypeHeader).toString();
}
if(!statusCode && reply->header(QNetworkRequest::ContentTypeHeader).toString().contains(QString("text/html"))) {
statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
redirectUrl = QUrl(reply->header(QNetworkRequest::LocationHeader).toUrl());
}
}
void Snapshot::doneLoading(bool)
{
// A reasonable waiting time for any script to execute
timer->start(3000);
}
void Snapshot::doneWaiting()
{
if( statusCode != 0 &&
statusCode != 301 &&
statusCode != 302 &&
statusCode != 303
) {
qDebug() << page->mainFrame()->url().toString();
qDebug() << page->mainFrame()->toHtml();
QImage image(page->viewportSize(), QImage::Format_ARGB32);
QPainter painter(&image);
page->mainFrame()->render(&painter);
painter.end();
image.save(*outputFilename);
delete outputFilename;
QApplication::quit();
}
else if(statusCode != 0) {
statusCode = 0;
qDebug() << "Redirecting to: " + redirectUrl.toString();
if(page->mainFrame()->url().toString().isEmpty()) {
qDebug() << "about:blank";
page->mainFrame()->load(this->redirectUrl); // No network activity after this
qDebug() << "Loading";
}
}
// This should ensure that the program never hangs
if(statusCode == 0) {
if(tries > 5) {
qDebug() << "Giving up.";
QApplication::quit();
}
tries++;
}
}
The problem was that the page I was testing was redirecting to https and had a self-signed certificate.
The solution was to make the QNetworkReply ignore the ssl errors:
void Snapshot::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
reply->ignoreSslErrors();
}