Qt: Post data to server - c++

I try to implement a REST client in order to get a Service Ticket from my server. For those of you who don't know CAS: A Service Ticket can be requested by showing a TGT. The TGT can be requested by a successful login basically. Maybe that is not even relevant.
I quess I have a error in my connect. My server is not even reacting to that connection and the reply is emptry. However for some reason reply->error() == QNetworkReply::NoErroris true.
What am I doing wrong?
bool Client::validateTGT(QString const & tgt) const
{
bool isValid = false;
QUrl url = QUrl("https://localhost:8943/cas/v1/tickets/" + tgt);
QUrl postData;
postData.addQueryItem("service", "https://test.de");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
//QNetworkAccessManager *networkManager = new QNetworkAccessManager();
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)), Qt::AutoConnection);
QNetworkReply *reply = manager->post(request, postData.encodedQuery());
QByteArray replyData = reply->readAll();
QString s_data = QString::fromAscii(replyData.data());
if (reply->error() == QNetworkReply::NoError)
{
isValid = true;
}
return isValid;
}
EDIT: replyFinished as requested
.h:
public slots:
void replyFinished(QNetworkReply *);
.cpp:
void CCASRESTClient::replyFinished(QNetworkReply *reply)
{
QByteArray replyData = reply->readAll();
serviceTicket = QString::fromAscii(replyData.data());
}

The slot replyFinished is called by the event loop when the reply has arrived from the server, this happens far after your function validateTGT has returned.
The manager object receives the reply and then emits the finished signal, this is when the slot replyFinished is called. It doesn't make sense to return a value from there. Just ask yourself, who is the caller function that will get this return value? validateTGT has already returned, thus it is not getting anything.
Your slot needs to be declared to return void, and you should do whatever you want to do with the reply in your replyFinished slot.
In general, if you have any slot that returns a value, the return value cannot be retrieved unless this slot was called as a normal function (Obviously this is not the case here).

Related

Wait till request finished Qt

My program is getting data from mysql table via json format through php script, that generating json.
My function sends to server post request (with some params) and getting response. With that all okay. Works fine.
But, when I want to get specified cell (data from json array), it takes empty string (program works fast and taking data from a string before it sets up)
Here's the code with post request:
void dbase::requestor(QString option)
{
curr_js = ""; //nulling string
QString urla = host+"transfer.php";
// /////////
QNetworkAccessManager * manager = new QNetworkAccessManager(this);
QUrl url(urla);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QUrlQuery params;
params.addQueryItem("api", key);
params.addQueryItem("option", option);
//QEventLoop loop;
connect(manager, SIGNAL(finished(QNetworkReply *)),this, SLOT(replyFinished(QNetworkReply *)));
//loop.exec(); tryed eventloop, but bcuz of this my replyFinished is not accessible. error code under this one
manager->post(request, params.query().toUtf8());
}
If using eventloop
QObject::connect: No such slot QEventLoop::replyFinished(QNetworkReply *)
replyFinished function (setting up variable)
void dbase::replyFinished(QNetworkReply *reply)
{
curr_js = reply->readAll();
}
Usage (where is problem)
QString req = "SOME REQUEST";
database.requestor(req);
if (database.db_cell (0,0) == "")
{
qDebug()<<database.db_cell (0,0) << " - EMPTY - "<<database.curr_js;
}
So in this case I'm getting
"" - EMPTY - ""
But if I getting data from string (created button for test), it's there:
{"0":["1", "Admin","hashpwd"],"1":["2", "Albert","hashpwd"]}
db_cell function
QString dbase::db_cell (int indexrow, int indexcols)
{
QJsonDocument sd = QJsonDocument::fromJson(curr_js.toUtf8());
QJsonObject setobj = sd.object();
QJsonValue qqq = setobj.value(QString(QString::number(indexrow))).toArray()[indexcols];
return qqq.toString();
}
As I see, problem is that program need to wait before getting a data from json-string.

How do I know a QNetWorkReply comes from which proxy?

It's a HTTP request sending method. When the goal website responses, httpFinished() will be called.
void HTTPClientBase:: HttpRequestGet()
{
network_manager.setProxy(proxy);
QNetworkRequest network_request;
network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_request.setUrl(URL);
reply = network_manager.get(network_request);
connect(reply, SIGNAL(finished(QNetWorkReply*)), this, SLOT(httpFinished(QNetWorkReply*)));
}
void HTTPClientBase::httpFinished(QNetWorkReply* reply)
{
// How do I know this reply comes from which proxy?
}
I can call HttpRequestGet() in a loop.
static HTTPClientBase myClient;
for(int i=0; i<20; i++)
{
myClient.setUrl("https:\\www.google.com");
myClient.setProxy("123.123.123.123:1234"); // The proxy changes every time in this loop.
myClient.HttpRequestGet();
}
When HTTPClientBase::httpFinished(QNetWorkReply* reply) is called, How do I know this reply comes from which proxy?
}
As per the QNetworkReply document, you can get the corresponding request using the member function QNetworkReply::request().
Anyway, QNetworkRequest has not member function of setProxy.
But if you are setting proxy for QNetworkAccessManager you can have a pointer to the corresponding manager by QNetworkReply::manager().
Notice the connect command. finished() has no QNetworkReply* argument so your connect command will also fail and you have to change HTTPClientBase::httpFinished(QNetWorkReply* reply) to HTTPClientBase::httpFinished().
...
connect(reply, SIGNAL(finished()), this,
SLOT(httpFinished()));
}
void HTTPClientBase::httpFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if(reply){
QNetworkAccessManager * manager = reply->manager();
QNetworkProxy proxy = manager->proxy();
// now you have the proxy
}
}
As you see, you have to use sender() to obtain the actual signal sender.
You need to create different QNetworkAccessManager instances for each proxy you have. If you have a proxy pool, create your QNetworkAccessManager instances first and choose them according to your specific needs.
If you don't want to create a new QNetworkAccessManager for each proxy, you can do something like this:
void HTTPClientBase:: HttpRequestGet()
{
network_manager.setProxy(proxy);
QNetworkRequest network_request;
network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_request.setUrl(URL);
reply = network_manager.get(network_request);
//new member variable: QHash<QNetworkReply*,QString> m_RequestToProxy;
m_RequestToProxy.insert(reply,proxy);
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
}
void HTTPClientBase::httpFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if(reply){
QString proxy = m_RequestToProxy.take(reply);
//check if proxy is valid
//and then do whatever you want
}
}
And another better solution is to set a property of reply and get it in slot.
void HTTPClientBase:: HttpRequestGet()
{
network_manager.setProxy(proxy);
QNetworkRequest network_request;
network_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
network_request.setUrl(URL);
reply = network_manager.get(network_request);
reply->setProperty("_proxy_",proxy);
connect(reply, SIGNAL(finished()), this, SLOT(httpFinished()));
}
void HTTPClientBase::httpFinished()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
if(reply){
QVariant v_proxy = reply->property("_proxy_");
//check if proxy is valid
if(v_proxy.isValid()){
QString proxy = v_proxy.toString();
//and then do whatever you want
}
}
}

Why am I getting bad request from google?

My objective
To exchange refresh token for access token from google using OAuth 2.
My code
bool Google_Account::Refresh_Access_Token_Using_Refresh_Token()
{
// Prepare Url
QUrl url(tr("https://www.googleapis.com/oauth2/v4/token"));
// Create request
QNetworkRequest request(url);
request.setRawHeader("Host:","www.googleapis.com");
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
// Create request body ClientID, ClientSecret, RefreshTokenString are class data members
QString RequestBody = tr("client_secret=%1&").arg(ClientSecret) +
tr("grant_type=refresh_token&")+
tr("refresh_token=%1&").arg(RefreshTokenString)+
tr("client_id=%1").arg(ClientID);
QByteArray array = RequestBody.toUtf8();
// Get reply
QNetworkReply *reply = mQNAM.post(request, array); // mQNAM is QNetworkAccessManager
// Set timeout to reply while waiting for reply finished
bool stop = false;
QTimer timer;
timer.setSingleShot(true);
QObject::connect(&timer, &QTimer::timeout, [&](){
qDebug()<<"Time out";
stop = true;
});
timer.start(5000);
// Wait till the response is completed
while(!reply->isFinished()){
QCoreApplication::processEvents();
if(stop){
qDebug()<<"Going to abort";
reply->abort();
}
}
// Check for reply
if(reply->isFinished()){
if(reply->error() != QNetworkReply::NoError){
qDebug()<<reply->readAll();
emit setMessage("Error: "+reply->errorString());
delete reply;
return false;
}
else{
QByteArray array = reply->readAll();
QJsonDocument document = QJsonDocument::fromJson(array);
QJsonObject obj = document.object();
access_token = obj.value("access_token").toString(); //access_token is class data variable
delete reply;
return true;
}
}
else{
delete reply;
return false;
}
}
The problem is that if I run this code in my windows 7 pc(Qt 5.11.1) everything is fine I get the access token but if i run in my raspberry pi(raspbian Qt 5.7) I get Error 400, Bad request from google. I tried using the access_token got from my windows pc and made other request such as to get the file list from google drive, they are working fine in raspberry, but only this I am having problem. What am I doing wrong?
P.S the code is refactored to the specific details only, in reality I am getting the client id and other keys from QSettings
This smells like you're getting a 400 code when Qt is requesting a token, because the login code you get when the flow returns to your app is URL-encoded. We wrote about How To Authenticate with Google SSO in Qt with some code samples including this bit that injects a parameter modifier in the flow:
google->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
// Percent-decode the "code" parameter so Google can match it
if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
QByteArray code = parameters->value("code").toByteArray();
(*parameters)["code"] = QUrl::fromPercentEncoding(code);
}
});
Do that before the initial request, but as suggested by #Giancarlo above, I'd rewrite your code to work asynchronously. It's simpler, and more reliable.

How to intercept AJAX-Requests within QtWebKit?

I want to intercept, inspect and (if needed) reject AJAX-Requests based on the Fingerprint of the SSL-Certificate. I use the QNetworkAccessManager::createRequest(...) function to issue requests. Everything works fine when I use QWebFrame::load(...). Even the content which is loaded within the request (like .css or .js files) emit signals. Unfortunately no AJAX-Requests emits any Signals. I know that the Signals are connected to the very same slots (for "normal" as well as AJAX-Requests) within MyNetworkAccessManager::createRequest(...) function.
QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
connect(reply, SIGNAL(readyRead()), this, SLOT(handleStarted()));
connect(reply, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(handleSslErrors(const QList<QSslError> &)));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleNetworkError()));
Why are AJAX Requests so different? Where can I access them?
From what I can tell, AJAX requests do emit the finished signal on QNetworkAccessManager. You need connect to the instance of QNetworkAccessManager on your QWebPage instance:
QWebPage *page = ui->webView->page();
QNetworkAccessManager *nam = page->networkAccessManager();
connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
QFile file;
file.setFileName(":/js/jquery-2.1.1.min.js"); // jQuery is loaded as a resource
file.open(QIODevice::ReadOnly);
QString jQuery = file.readAll();
file.close();
ui->webView->load(QUrl("about:blank"));
QWebFrame *frame = m_page->mainFrame();
frame->evaluateJavaScript(jQuery); // load jQuery
// check that jQuery is loaded
frame->evaluateJavaScript("$(document).ready(function() { alert('jQuery loaded!'); });");
// do an AJAX GET
frame->evaluateJavaScript("$.ajax({"
"url: 'http://www.json-generator.com/api/json/get/cqkXBAEoQy?indent=2',"
"method: 'GET',"
"dataType: 'json'"
"}).done(function (data) {"
"for (var i = 0; i < data.length; i++) {"
"alert(data[i].name);"
"}"
"}).error(function (data) { alert('AJAX error'); });");
Then you can monitor replies in the replyFinished slot like so:
void MainWindow::replyFinished(QNetworkReply *reply)
{
QByteArray bytes = reply->readAll();
QString str = QString::fromUtf8(bytes.data(), bytes.size());
QString replyUrl = reply->url().toString();
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << statusCode;
qDebug() << replyUrl;
qDebug() << str;
}
I have noticed that jQuery AJAX's done promise doesn't seem to execute when you do anything with the QNetworkReply, but you can see that the request actually finishes in the debug console.
See my GitHub repository to try out the above code: https://github.com/pcmantinker/QtWebkitAJAX
As far as blocking connections based on SSL certificates, you'd have to subclass QNetworkAccessManager and override QNetworkAccessManager::createRequest. Something like this could work:
QNetworkReply *CustomQNetworkAccessManager::createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
{
QNetworkRequest req(request);
QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
QSslConfiguration *sslConfig = reply->sslConfiguration();
QList<QSslCertificate> sslCaCerts = sslConfig->caCertificates();
// do something with sslCaCerts
return reply;
}

how to return an Qt object from the result of an thread (Qtfutur)

i'm trying to load some data from a server and fill a Qt list. i want to run the dowloanding in a thread. so there is the code of:
principal function in the App.cpp
loadInterestPlaces(QString& urlInterestPlaces) {
LoadData* Data = new LoadData(urlInterestPlaces);
QFuture< list <InterestPlace *> > future = QtConcurrent::run(Data,
&LoadData::startLoading);
// Invoke our onLoadingFinished slot after the loading has finished.
bool ok = connect(&m_watcher, SIGNAL(finished()), this,
SLOT(onLoadingFinished()));
Q_ASSERT(ok);
Q_UNUSED(ok);
// starts watching the given future
m_watcher.setFuture(future);
}
void ApplicationUI::onLoadingFinished() {
qDebug() << "Loading finished";
interestPlacesList = m_watcher.future().result();
qDebug() << "List size= " << interestPlacesList.size();
}
}
the LoadData.cpp file : this is the code of the startloanding function :
std::list<InterestPlace *> LoadData::startLoading()
{
QNetworkAccessManager* netManager = new QNetworkAccessManager(this);
const QUrl url(_URL);
QNetworkRequest request(url);
QNetworkReply* reply = netManager->get(request);
netManager->moveToThread(this->thread());
netManager->setParent(this);
bool ok = connect(reply, SIGNAL(finished()), this, SLOT(onReplyFinished()));
qDebug() << reply->isFinished();
Q_ASSERT(ok);
Q_UNUSED(ok);
qDebug() << "load data: liste size" <<interestPlacesList.size();
return interestPlacesList;
}
Finally inside the SLOT onreplyfinished i parse the data and fill the list.
But the problem here, the QFuture is finished before the downloading so that the list is always empty.
How could i return the list filled just after the execution of the onReplyFinished ?
You may be making this more complex than you need to. Here is what I do to get data off the web. In my case I'm downloading a large ZIP file, writing it out and then unziping it, but the basic steps are the same (I have omitted a lot of my specific code for clarity):
void HtmlDownloader::startDownload() {
// Creates the network request and sets the destination URL
QNetworkRequest request = QNetworkRequest();
request.setUrl(mUrl);
// Creates the network access manager and connects a custom slot to its
// finished signal. Checks the return value for errors.
QNetworkAccessManager *networkAccessManager =
new QNetworkAccessManager(this);
bool c = connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(requestFinished(QNetworkReply*)));
Q_ASSERT(c);
// Indicate that the variable res isn't used in the rest of the app, to prevent
// a compiler warning
Q_UNUSED(c);
// Sends the request
QNetworkReply *reply = networkAccessManager->get(request);
c = connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(onDownloadProgress(qint64,qint64)));
Q_ASSERT(c);
}
void HtmlDownloader::requestFinished(QNetworkReply *reply) {
// Handle the reply data...
// Check the network reply for errors
if (reply->error() == QNetworkReply::NoError) {
// use reply->readAll() or reply->read() to get the returned data
// and process into a list. The list can't be returned but could
// be sent as a payload of a signal, or stored in a data member and
// a signal sent to indicate it is available.
}
reply->deleteLater();
}
void HtmlDownloader::onDownloadProgress(qint64 bytesRecieved, qint64 bytesTotal) {
emit downloadProgress(bytesRecieved, bytesTotal);
}