Memory access error when using QNetworkManager - c++

I'm fairly new to C++ (though I have some experience with C) as well as QT. I'm trying to make a program that POSTs to a website when the user clicks a button, but whenever I try to access QNetworkManager I get a memory access error.
The code for my request object is as follows (trimmed slightly to show the important bits):
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include "cJSON.h"
class unifiedRequests: public QObject {
Q_OBJECT
public:
// Members
QString access_token = "";
bool admin = false;
// Methods
explicit unifiedRequests(QObject *parent=0);
QNetworkReply* login_request(QString *email, QString *password);
signals:
public slots:
void login_complete(QNetworkReply *reply);
void sslErrorHandler(QNetworkReply*, const QList<QSslError> & );
private:
QNetworkRequest make_headers(QByteArray endpoint);
QNetworkRequest make_headers(QByteArray endpoint, QByteArray *access_token);
};
QNetworkRequest unifiedRequests::make_headers(QByteArray endpoint) {
QString url = endpoint.prepend("https://dali.vpt.co.uk");
QNetworkRequest request = QNetworkRequest(url);
qDebug() << "Setting Headers";
request.setRawHeader("User-Agent", "Desktop Client Debug");
request.setRawHeader("Content-Type", "application/json");
qDebug() << "Set headers successfully.";
return request;
}
void unifiedRequests::sslErrorHandler
(QNetworkReply* reply, const QList<QSslError> & errors) {
qDebug() << "Ignoring SSL Errors";
};
QNetworkReply* unifiedRequests::login_request
(QString *email, QString *password) {
QNetworkRequest request = make_headers("/api/auth");
qDebug() << "Making JSON";
cJSON *login_json; //The body of the request
login_json = cJSON_CreateObject();
cJSON_AddStringToObject(login_json, "email", email->toUtf8());
cJSON_AddStringToObject(login_json, "password", password->toUtf8());
qDebug() << "Made JSON: ";
qDebug() << cJSON_Print(login_json);
QNetworkAccessManager *manager = new QNetworkAccessManager;
//The object we use to send the request and receive the reply
qDebug() << "Turning off SSL";
connect(manager,
SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )),
this,
SLOT(sslErrorHandler(QNetworkReply*, const QList<QSslError> & )));
qDebug() << "POSTing login.";
QNetworkReply *reply = manager->post(request, cJSON_Print(login_json));
qDebug() << "Connecting signal to slot.";
QAbstractSocket::connect(manager, SIGNAL(finished(QNetworkReply * )),
this, SLOT(login_complete(QNetworkReply * )));
cJSON_Delete(login_json);
return reply;
}
I'm creating the unifiedRequests object by calling:
unifiedRequests requestObj;
in a different file. It crashes out on the line where I try to turn off SSL (we're using a self-signed certificate, so I need to do this in order to make the request). Any thoughts?
Thank you!

You create the unifiedRequests object by calling "unifiedRequests requestObj;", this object will be deleted when the variable "requestObj" goes out of scope.
So, when the signal will be received, the object will be already destroyed.
Try to create your unifiedRequests object by calling "unifiedRequests* requestObj = new unifiedRequests();".
Of course, you need to call "delete requestObj;" somewhere to destroy this object. Where and when depend on your application (when you don't need this object anymore).
To understand the difference, look at here : http://www.tutorialspoint.com/cplusplus/cpp_dynamic_memory.htm
Or google for "C++ heap / stack / dynamic allocation"

Related

How to run a post request in qt main()

I would like to do a simple post request int main.cpp. it would seem the when i run the application it would not execute the code and just skips it.
I tried using the qt debugger but the code below after the debugger start it just finishes right after.
I have tested my api with postman and knows it works
main.cpp
#include <iostream>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QNetworkInterface>
using namespace std;
int main()
{
QByteArray jsonString = "{\"ipaddr\": "+ QByteArray::number(9) + ",\"transactionType\":"+QByteArray::number(10) + ",\"idEmployee\":"+QByteArray::number(10) +"}";
QNetworkRequest request(QUrl("http://192.168.1.25:3000/classlog/pi"));
request.setRawHeader("Content-Type", "application/json");
QNetworkAccessManager * manager = new QNetworkAccessManager();
manager->post(request, jsonString);
}
.pro
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
QT += network core
SOURCES += \
main.cpp
I expect that i would be able to receive the request in my server, but i am not receiving any. Thank you
Qt uses an event system. Your the network manager will only schedule a request that will be handled in an event loop. This is also where the response is received.
You need a running event loop (and in fact, a QCoreApplication object, you sould get a warning when executing your code).
#include <QtNetwork>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QNetworkAccessManager mgr;
QNetworkRequest req(QUrl("http://stackoverflow.com"));
auto *resp = mgr.get(req);
QObject::connect(resp, &QNetworkReply::finished, [&]() {
qDebug() << "FINISHED";
if (resp->error() != QNetworkReply::NoError)
qDebug() << "Error: " << resp->errorString();
else
qDebug() << "Status: " << resp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString();
// Stop when a response is received
app.quit();
});
// This will start the event loop that will eventually send the request and receive the response.
// It will run until you call app.quit()
return app.exec();
}
you are almost there:
connect the signals:
QtObject::connect(your_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onResult(QNetworkReply *)));
send something:
QNetworkRequest request(your_URL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QByteArray byteArray;
byteArray.append(your_json);
your_manager->post(request, byteArray);
read the answer in the slot:
void FOO_CLASS::onResult(QNetworkReply* reply)
{
QString resp = QString::fromUtf8(reply->readAll());
}
edit
:
QObject::connect(your_manager, &QNetworkAccessManager::finished, [](QNetworkReply * r){
QString x{r->readAll()};
//foo1
auto l{x.length()};
//foo2
});

How to use QNetworkManager for REST api?

I would like to make a class for accessing data via REST API, for example:
class MeteoStation{
int getLatestTemperature();
int getLatestPessure();
private:
QNetworkManager nmng;
}
How could I implement this methods? Usually I was using something like:
int MeteoStation::getLatestTemperature(){
...
QEventLoop eventLoop;
connect(&m_nam,SIGNAL(finished(QNetworkReply*)),&eventLoop,SLOT(quit()));
QNetworkReply *reply = m_nam.get( req );
eventLoop.exec();
reply->readAll()
...
}
But since using inner QEventLoop is not recommended, how should I see to whom the response belong to?
MeteoStation::MeteoStation(){
connect(&nmam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseNetworkResponse(QNetworkReply*)));
}
void MeteoStation::parseNetworkResponse( QNetworkReply *finished )
{
QByteArray data = finished->readAll();
...
Yes and it would be nice to have the class thread save. How are you solving that in your code?
How bad is making the call synchronous with:
QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));
QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();
I've resolved my problem using QCoreApplication::processEvents(). The response is there within ms and I'm able to implement functionality close to libcurl.
QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));
QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();
The Qt docs should provide all info you need.
You creat a nam, connect the finished signal, send the request.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseNetworkResponse(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
Detecting to which request a reply belongs should not be too hard. The reply contains the url. It might be different, but not that different:
...but for a variety of reasons it can be different (for example, a
file path being made absolute or canonical).
QUrl QNetworkReply::url() const
Returns the URL of the content downloaded or uploaded. Note that the
URL may be different from that of the original request.

Qt Download File - QNetworkAccessManager, not getting data

I'm trying to have my application download a file from a URL, typically an EXE or a Jar, not that this should change much though. I have this all running in a thread, but I don't think that will make a difference (if it does let me know).
So Do_Download is my function that creates the manager, sets the URL and request, and performs get. I then try to connect the finished signal to the slot the will write the file.
void DownloadManager::Do_Download() {
QNetworkAccessManager *netManager = new QNetworkAccessManager(this);
QUrl url(install_mirror); //istall_mirror is the URL provided by user
QNetworkRequest req(url);
QNetworkReply *reply = netManager->get(req);
connect(reply, SIGNAL(finished()), this, SLOT(writeData()));
}
My writeData function checks for errors, and if there are no errors it writes the data to file.
void DownloadManager::writeData() {
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply) {
if (reply->error() == QNetworkReply::NoError) {
QFile file(location);
if(file.open(QIODevice::WriteOnly)) {
file.write(reply->readAll());
} else {
errorMessage = "Error writing downloaded file for mirror installation";
}
} else {
//get http status code
int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
errorMessage = "HTTP Error code while downloading from mirror: " + httpStatus;
}
reply->deleteLater();
} else {
errorMessage = "Error downloading file from installation mirror";
}
}
The problem being there is no data being written. It just creates a 0Kb file.
I tried adding a download progress slot so I could see what was going on recieving the data. So I added this to my Do_Download method.
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(DL_Progress(qint64,qint64)));
void DownloadManager::DL_Progress(qint64 recieved, qint64 total) {
std::cout << recieved << " / " << total << endl;
}
The output displays one time as 0 / 01
What am I doing wrong?
The only problem I see in your code is you are not waiting for the download to be finished. The NetworkRequest object would be destructed at the end of function call.
So, I would rewrite Do_Download like this (QEventLoop syncronizes the network request):
void DownloadManager::Do_Download() {
QEventLoop eventLoop;
QNetworkAccessManager *netManager = new QNetworkAccessManager(this);
QUrl url(install_mirror); //istall_mirror is the URL provided by user
QNetworkRequest req(url);
QNetworkReply *reply = netManager->get(req);
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
writeData(reply);
}

Send HTTP post request BlackBerry 10

I am developing an app on BB 10 based on C++ where I need to send HTTP post requests to the server and retrieve some JSON data. Are there some framework classes that help you send HTTP post requests to the server? Any links to code etc.? Thanks.
I can't remember which sample app I found this code in, but this worked for what I needed in my app.
Add the following in c++
header file (.hpp)
public:
Q_INVOKABLE void doNetworkRequest(QString url);
signals:
void networkReply(const QVariantMap &data);
void networkError();
private Q_SLOTS:
void handleNetworkData(QNetworkReply *reply);
private:
QNetworkAccessManager networkManager;
Then in add this in your main file (.cpp)
this goes inside the main app function
// Hook this signal so we can respond to network replies
connect(&networkManager, SIGNAL(finished(QNetworkReply *)), this,
SLOT(handleNetworkData(QNetworkReply *)));
add these functions:
void Top12Wines::doNetworkRequest(QString url)
{
qDebug() << "Request URL " << url;
QUrl qurl = url;
networkManager.get(QNetworkRequest(qurl));
}
void Top12Wines::handleNetworkData(QNetworkReply *reply)
{
if (!reply->error()) {
qDebug() << "Got network data";
// Let's get ALL the data
const QByteArray response(reply->readAll());
JsonDataAccess jda;
QVariantMap results = jda.loadFromBuffer(response).toMap();
emit networkReply(results);
} else {
qDebug() << "Got network error";
emit networkError();
}
// Cleanup
reply->deleteLater();
}
Then in your QML you can access it like so:
_App.networkReply.connect(checkVersion); //
_App.networkError.connect(checkVersionError);
_App.doNetworkRequest("http://myserver/version.json");
function checkVersion(data)
{
_App.networkReply.disconnect(checkVersion); //disconnect links after retrieving data
_App.networkError.disconnect(checkVersionError);
var newVersion = data.version;
}
function checkVersionError()
{
_App.networkReply.disconnect(checkVersion); //disconnect links after retrieving data
_App.networkError.disconnect(checkVersionError);
//do something to alert user that an error occurred.
}

QNetworkReply reply has no members in finishedSlot

Qt Creator is used as the ide for this small app that is being developed
I am attempting to use QNetworkAccessManager to retrieve some information from a website. After the request is 'posted' to the web, the finished() signal is triggered, however the pointer that is passed to the finishedSlot() function does not appear to be pointing to an instantiated object, it is just address of the ponter. The code for the button click that starts the request and the code for the finishedSlot() method is shown below.
In the watch window, I would have expected to see a triangle next to 'reply' that when expaned would show all the data member of QNetworkReply object. Instead it has a single value of #0x80c770 which looks like the pointer address.
I'd appreciate input from anyone who can help me to understand why my pointer doesn't appeart to be pointing the the QNetworkReply object.
void MainWindow::on_btnGetOAuthToken_clicked()
{
QUrl serviceUrl("https://api.ProPhotoWebsite.com/services/oauth/authorize.mg");
QUrl postData;
postData.addQueryItem("method", "ProPhotoWebsite.auth.getRequestToken");
postData.addQueryItem("oauth_consumer_key", "AAAAAAAAAAAAAAAAAAAAAAAA"); //example key
postData.addQueryItem("oauth_nonce",QUuid::createUuid().toString());
postData.addQueryItem("oauth_signature_method","PLAINTEXT");
postData.addQueryItem("oauth_signature","999999999999999999999999999"); //example
postData.addQueryItem("oauth_timestamp", QString::number(QDateTime::currentMSecsSinceEpoch()/1000));
postData.addQueryItem("oauth_version","1.0");
//...
QNetworkRequest request(serviceUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
// Call the webservice
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
connect(nam, SIGNAL(finished(QNetworkReply*)),
SLOT(finishedSlot(QNetworkReply*)));
nam->post(request,postData.encodedQuery());
}
void MainWindow::finishedSlot(QNetworkReply *reply)
{
// Reading attributes of the reply
// e.g. the HTTP status code
QVariant statusCodeV =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
// Or the target URL if it was a redirect:
QVariant redirectionTargetUrl =
reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
// see CS001432 on how to handle this
// no error received?
if (reply->error() == QNetworkReply::NoError)
{
QByteArray bytes = reply->readAll(); // bytes
QString string(bytes); // string
ui->lblWarning->setText(string);
}
else
{
// handle errors here
}
// need to dispose reply
delete reply;
}