QNetworkReply error "Connection closed" when getting a simple URL - qt

I'm trying to make a simple file downloader. Here is my code segment for downloading a URL; This code works fine, if I try to get simple URLs like "http://stackoverflow.com".
But when I specify URLs which redirects, it throws an error saying "Connection closed".
//Sets a sample URL which redirects.
QString url = "http://downloads.sourceforge.net/project/gretl/gretl/1.9.92/gretl-1.9.92.tar.xz"
QNetworkAccessManager * networkAccessManager = new QNetworkAccessManager(this);
QNetworkRequest * networkRequest = new QNetworkRequest(QUrl(url));
//Getting the URL
QNetworkReply * networkReply = networkAccessManager->get(*networkRequest);
Additionally I've connected the following signal-slot to recognize network errors:
//Connects networkReply object's error signal to a slot which prints the error.
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(printError(QNetworkReply::NetworkError)));
Here is the slot which prints errors if networkReply's error signal is emitted:
void printError(QNetworkReply::NetworkError code)
{
qDebug() << "Network Error Code: " << code;
qDebug() << networkReply->errorString();
}
Because of this error, networkReply object never receives any header.
My need is to read the HTTP status code by this method: QNetworkReply::attribute(QNetworkRequest::HttpStatusCodeAttribute)
I can't read the HTTP Status code, because I don't get the headers.
Can anyone say whats going on?
Thanks in advance! :)

Here and here are good examples of handling this scenario - basically you add a slot for finished signal, then check the HTTP status code for 301 and/or QNetworkRequest::RedirectionTargetAttribute attribute to see if you are being redirected and if so issue a new request for the redirect link.

Related

QNetworkAccesssManager icinga / nagios acknowledge

I have a function who sending ack into the icinga / nagios server.
Function - sendAcknowledge
void MNetworkConnector::sendAcknowledge(QString service, QString host)
{
QNetworkAccessManager *ackmanager;
ackmanager = new QNetworkAccessManager();
QString ackcommand = "http://nagioscore.demos.nagios.com/nagios/cgi-bin/cmd.cgi?cmd_typ=34&cmd_mod=2&host=#host#&service=#service#&com_author=nagiosadmin&com_data=Sent:+mMonitor&btnSubmit=Commit";
service = service.replace(" ", "+");
ackcommand = ackcommand.replace("#host#", host).replace("#service#", service);
connect(ackmanager, SIGNAL(finished(QNetworkReply*)),this, SLOT(replyFinished2(QNetworkReply*)));
QNetworkRequest myReq;
QUrl myUrl(ackcommand);
myUrl.setUserName("nagiosadmin");
myUrl.setPassword("nagiosadmin");
myReq.setUrl(myUrl);
myReq.setRawHeader("Referer", "http://nagioscore.demos.nagios.com/nagios/cgi-bin/cmd.cgi");
ackmanager->get(myReq);
}
Function - replyFinished2
void MNetworkConnector::replyFinished2(QNetworkReply *r)
{
qDebug() << "reply output:" << r->readAll();
}
System returned several errors.
If I test this source on my corporate icinga server, I have error:
Not all commands could be send off successfully - Not Authorized
If I test it manualy on my corporate icinga server, I have error: Error: This appears to be a CSRF attack! The command wasn't issued via Classic-UI itself!
If I test it manualy on nagios test site, I have no error. Set service ack is Ok.
If I test this source I have error:Sorry, but you are not authorized to commit the specified command.
What's wrong? Thank you for all your ideas. At first I need solved problem in Icinga, the nagios test page I used only as alternative tests.
When the website requests authentication QNetworkAccessManager will emit the authenticationRequired() signal. Try connecting the signal with a slot and then set the username and password on the QAuthenticator object passed as argument to the slot.
Connection:
connect(ackmanager, SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), this, SLOT(authenticationRequired(QNetworkReply *, QAuthenticator *)));
Slot:
void MNetworkConnector::authenticationRequired(QNetworkReply *r, QAuthenticator *authenticator)
{
authenticator->setUser("nagiosadmin");
authenticator->setPassword("nagiosadmin");
}
The problem was sensitive to uppercase and lowercase letters in the HOST.

Logging into a remote website with QT5

I am attempting to create an application that can login to a website. The specific website is:
http://adfast.biz
Here is the code I am currently using:
void MainWindow::http_finish(QNetworkReply *reply)
{
int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (code >= 300 && code < 400)
{ //HTTP 3XX codes are redirections
QUrl redirectTo = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
reply->manager()->get(QNetworkRequest(redirectTo));
return;
}
if (reply->error() == QNetworkReply::NoError)
{
QString Msg = QString::fromUtf8(reply->readAll());
if (cdone == 0)
{//Runs only once, causing a reload of the main page
++cdone;
QUrl URL("http://adfast.biz");
QNetworkRequest QNR(URL);
reply->manager()->get(QNR);
QMessageBox::information(0,"1)" + QString::number(code),Msg);
return;
}
QMessageBox::information(0,"2)" + QString::number(code),Msg);
}
else
{
QMessageBox::information(0,"Error:",reply->errorString());
}
reply->deleteLater();
}
void MainWindow::on_Send_clicked()
{
QNetworkAccessManager* MNAM = new QNetworkAccessManager(this); //Stored within QNetworkReply->manager()
connect(MNAM,SIGNAL(finished(QNetworkReply*)),this,SLOT(http_finish(QNetworkReply*)));
QUrlQuery postData;
postData.addQueryItem("email","mail#mail.net");
postData.addQueryItem("senha","Password");
postData.addQueryItem("logar","ok");
QUrl URL(ui->TXT_Input->toPlainText());
URL.setQuery(postData);
QNetworkRequest QNR(URL);
QNR.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
MNAM->post(QNR,URL.toEncoded());
}
I am guessing that I either am sending the information the wrong way or that I need to possibly manage cookies? Both responses come back with HTTP status code: 200. The first comes with no source the second comes with the full web-source but is NOT logged in. I am positive that the user-data being sent is correct, but not that it is being sent correctly.
Edit:
I've changed a little, with no luck. First I have added a cookie-jar using:
QNetworkAccessManager* MNAM = new QNetworkAccessManager(this); //Stored within QNetworkReply->manager()
QNetworkCookieJar* cJar = new QNetworkCookieJar;
MNAM->setCookieJar(cJar);
connect(MNAM,SIGNAL(finished(QNetworkReply*)),this,SLOT(http_finish(QNetworkReply*)));
Then, I tested to see if any cookies are being received using the following code at the top of MainWindow::http_finish:
QList<QNetworkCookie> cookies = reply->manager()->cookieJar()->cookiesForUrl(QUrl("http://adfast.biz/"));
QMessageBox::information(0,"Cookies",QString::number(cookies.count()));
I want to add that the post is being sent to: http://adfast.biz/login (That is the value of: ui->TXT_Input->toPlainText()) But it seems that I can't get this to login at all. And I am not sure what I'm missing.
So, I did manage to login into this site with testguest#yahoo.com and mypassword12. But I can only offer very quick and dirty code, which explains how. The 'polishing' you must do yourself. :-)
QFile f("/tmp/cookie.txt");
f.open(QIODevice::ReadOnly);
QDataStream s(&f);
while(!s.atEnd()){
QByteArray c;
s >> c;
QList<QNetworkCookie> list = QNetworkCookie::parseCookies(c);
qDebug() << "eee" << list;
jar->insertCookie(list.at(0));
}
connect(MNAM,SIGNAL(finished(QNetworkReply*)),
this,SLOT(http_finish(QNetworkReply*)));
QUrlQuery postData;
postData.addQueryItem("email","testguest#yahoo.com");
postData.addQueryItem("senha","mypassword12");
postData.addQueryItem("logar","ok");
//QUrl URL("http://adfast.biz/");
//QUrl URL("http://adfast.biz/anuncios_telexfree");
URL.setQuery(postData);
QNetworkRequest QNR(URL);
QNR.setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
MNAM->get(QNR);
Above code reads the cookies, which are written in the http_finish slot below into /tmp/cookie.txt.
The first time you run this code, you must uncomment the first URL, the second time, when you have your cookies, the second URL. Ignore that I made your post into a get. I did it just for debugging reasons.
void MainWindow::http_finish(QNetworkReply *reply){
qDebug() << reply->readAll();
QList<QNetworkCookie> list =
MNAM->cookieJar()->cookiesForUrl(QUrl("http://adfast.biz/"));
QFile f("/tmp/cookie.txt");
f.open(QIODevice::ReadWrite);
for(int i = 0; i < list.size(); ++i){
QDataStream s(&f);
s << list.at(i).toRawForm();
}
f.close();
}
The code above writes the cookies from http://adfast.biz/ into /tmp/cookie.txt. Below an example of a cookie, which I received:
2-Sep-2013 16:41:39 GMT; domain=adfast.biz; path=/3 16:41:39 GMT;
domain=adfast.biz; path=/n=adfast.biz; path=/
Summary: You must connect to http://adfast.biz/ to get your cookie. When you have it, you must again connect, but this time to http://adfast.biz/anuncios_telexfree.
You may need to add a QNetworkCookieJar to your QNetworkAccessManager for keeping and sending cookies between requests and responses. You can set your cookie jar by using QNetworkAccessManager::setCookieJar(QNetworkCookieJar* cookieJar). Have a look at the requests being sent using HttpFox in Firefox and you can see if there are any cookies being sent back and forth.
Try to connect:
connect(&MNAM,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*)));
And create a slot:
void TrackerClient::provideAuthentication(QNetworkReply *reply,
QAuthenticator *auth){
Q_UNUSED(reply);
auth->setUser(<your username>);
auth->setPassword(<your password>);
}
Ok, second try. The idea to add a cookiejar is correct, but just adding the jar is not enough. I tried the following with another site, which requires login. I did work. For your site I don't have login and password, and since it is not English, it is a little bit too hard for me for a quick help. :-)
How the login procedure works on "my" site.
You go to the main url, e.g. www.mysite.something.
The site asks you for a cookie. You have none.
Your are redirected (status 302 temporary moved) to a
page www.mysite.something/takelogin.php <--- example.
You enter your credentials. You already do this in your postdata.addQuery calls.
You send your post. If your creds are ok, the site sends you a cookie.
So far so good. What did you wrong? Now that you have the cookie you must again go to www.mysite.something. You are not automatically logged in and redirected to the main page of your site. And keep in mind, that cookies in QNetworkCookieJar are not stored on disk. They are only kept in memory. So, if your jar/MNAM is deleted, everything is gone.
You can easily store your received cookies to disk using the toRawForm() method of QNetworkCookie. To restore the cookie use the static QNetworkCooky::parseCookies(const QByteArray &cookieString) method.
Oh, almost forgot: The main url www.mysite.something did not send any cookies. I had to follow the redirect www.mysite.something/takelogin.php.
Disclaimer: It worked on my site. I don't know, if this is general for all sites, which require a login.
Checked with wrong creds: I was redirected to adfast.biz/login. This seems to be your redirect login page. More I cannot do without real creds.

QNetworkAccessManager sends GET two times

I've got some class to interfere with HTTP-server.
Here is meaningfull code parts:
const QString someClass::BASEURL = QString("http://127.0.0.1:8000/?");
someClass::someClass():
manager(new QNetworkAccessManager(this))
{
}
QNetworkReply *someClass::run(QString request)
{
qDebug() << request;
QEventLoop loop;
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));
QNetworkReply *res = manager->get(QNetworkRequest(QUrl(BASEURL + request)));
loop.exec();
return res;
}
When I call method run(), sometimes (not every time) the are two identical GET-requests
(I looked with tcpdump). qDebug() executes 1 time.
Is there some error in my code? I can't see any possible explanation.
UPDATE:
After some tcpdump ouptut research.
After second request it sends packet with RST flag as an answer to FIN.
But I still can see no difference in TCP-streams that triggers the problem and that doesn't.
F.e. here is wireshark's output. Stream 8 went well. Stream 11 was duplicated with Stream 12.
I'm stuck with this. Maybe it's some protocol errors from server-size, I'm not sure. Or maybe it's a bug in QNetworkAccessManager.
Have you tried rewriting you code to be more asynchronous without using QEventLoop in a local scope? Your code looks good to me, but there might be some weird QT bug that you running into in the way it queues up requests for processing and using QEventLoop in the local scope. I usually use QNetworkAccessManager in the following manner to send GET and POST requests:
void someClass::run(QString request)
{
qDebug() << request;
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_request_complete(QNetworkReply*)));
QNetworkReply *res = manager->get(QNetworkRequest(QUrl(BASEURL + request)));
}
void someClass::on_request_complete(QNetworkReply* response)
{
// Do stuff with your response here
}

How to use post() in qt?

This is my program. In this program I want to send request to a website (for example: http://www.adobe.com/products/muse.html)
I want to show the html code that return me in plain text box.
QUrl url("http://www.adobe.com/products/muse.html")
I want to give html code in "thisfile"
file.setFileName("thisfile.html");
if (!file.open(QIODevice::WriteOnly))
{
std::cerr << "Error: Cannot write file "
<< qPrintable(file.fileName()) << ": "
<< qPrintable(file.errorString()) << std::endl
return false;
}
http.setHost(url.host(),80);
http.post(url.toString(),"term=yyyy&loc=en_us&siteSection=products%3Amuse",&file);
This code doesn't work correctly and when I show the file give me false html code. What do I have to do?
Use http.get() instead of http.post() as POST method requires to set other Headers used by server.
QHttp::get() method is asynchronous too.
As your case is simple enough just to retrieve HTML response, you should go for HTTP GET IMHO. See difference between GET and POST method.
And if you have to use HTTP POST only, then check this.
QNetworkRequest request;
request.setUrl(QUrl("thisfile.html"));
QNetworkReply *reply = manager->post(request, "term=yyyy&loc=en_us&siteSection=products%3Amuse");
connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
Look at QNetworkAccessManager at qt docs
You have to read information and save it to file at readyRead function

Reading HTTP headers

I am trying to connect my application with a web service and here ,a user suggested to send custom headers back to my application.
I am using this code
void Coonnec::serviceRequestFinished(QNetworkReply *reply)
{
QByteArray bytes = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Reply error: " + reply->errorString();
}
else
{
qDebug() << "Uploaded: " + QDateTime::currentDateTime().toString();
qDebug() << reply->rawHeaderList();
}
reply->close();
bytes.clear();
reply->deleteLater();
}
from php i send this header
header('XAppRequest-Status: complete');
When running the application i can see that i get this header but i can't take the value of it cause
reply->rawHeader(bytes);
returns nothing.
How can i take the value 'complete'?
I suggest to connect a slot to the void QNetworkReply::metaDataChanged () signal of your reply.
The Qt doc says
This signal is emitted whenever the metadata in this reply changes.
metadata is any information that is not the content (data) itself,
including the network headers. In the majority of cases, the metadata
will be known fully by the time the first byte of data is received.
However, it is possible to receive updates of headers or other
metadata during the processing of the data.
I do use web-services/client with Qt and I noticed that some header's information are not available when I expected it to be ! I had to 'wait' for this signal to check the header content.

Resources