Seite 1 von 1

QN..A..Manager sendet keinen Body nach Auth-Anfrage vom serv

Verfasst: 10. Dezember 2009 23:41
von MatthiasMit2T
Hallo, dies ist mein erster Post hier, also kurz zu mir:

Ich bin Informatikstudent und gezwungen, nun mit Qt zu arbeiten.

Was bei mir am Ende rauskommen muss ist ein Webdav Client. Dieser muss erstmal nur GET und PUT können. Da QHttp als deprecated markiert ist, nutze ich also QNetworkAccessManager.

GET funktioniert, PUT macht mir allerdings zu schaffen.
Ich benutze Qt 4.5.3.9 auf einem FC11. gcc aktuell und Eclipse als GUI.

Ich habe folgenden Code (dieser wird ohne Fehler kompiliert):

Code: Alles auswählen

this->localFile = new QFile("./" + filename);
    if(! QFile::exists(this->localFile->fileName())){
      QMessageBox::warning(this->downloadManager, "Datei fehlt", QString("Die Datei %1 konnte nicht gefunden werden.").arg(filename).toLatin1(), QMessageBox::Ok);
      return;
    }
    qDebug("File exists, upload!");
    this->localFile->open(QIODevice::ReadOnly);

    this->url = new QUrl();
    this->url->setScheme("https");
    this->url->setHost(servername);
    this->url->setPort(port);
    this->url->setPath("/upload-webdav/" + filename);
    this->url->setUserName("test");
    this->url->setPassword("test");

    this->downloadManager = mgr;

    this->http = new QNetworkAccessManager(this);

    connect(this->http, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(slotSslErrors(QNetworkReply*, QList<QSslError>)));
    connect(this->http, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*)));
    connect(http, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));

    qDebug("upload begins to " + this->url->host().toLatin1() + QString(" data:").toLatin1() + this->localFile->readAll());
    this->localFile->reset();

    QSslConfiguration *ssl = new QSslConfiguration();
    QList<QSslCertificate> certs;
    QFile *pem = new QFile("./cert.pem");
    pem->open(QIODevice::ReadOnly);
    certs = QSslCertificate::fromDevice(pem, QSsl::Pem);
    ssl->setCaCertificates(certs);
    ssl->setPeerVerifyMode(QSslSocket::QueryPeer);

    QNetworkRequest req(*this->url);
    req.setSslConfiguration(*ssl);

    this->rpl = this->http->put(req,this->localFile->readAll());
    this->rpl->ignoreSslErrors();

    this->rpl->setSslConfiguration(*ssl);

    connect(this->rpl, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError(QNetworkReply::NetworkError)));
    connect(this->rpl, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(sendStatus(qint64, qint64)));
    connect(this->rpl, SIGNAL(finished()), this, SLOT(replyFinished()));

    qDebug("Webdav Uploader inited");
Dieser ist über 2 Tage Trial and Error entstanden und konnte leider noch keine Datei uploaden. Zur Erklärung noch:

Der SslConfig kram ist dann reingenommen worden, weil ich in dem entsprechenden Slot viele SslError Meldungen bekommen habe. Ansonsten fällt auf, dass die Verbindung nicht etwa mit finished() Signal beendet wird, sondern nach eineiger Zeit (vielleicht 2-5 Minuten) mit Network Error 2 (Host hat die Verbindung getrennt) beendet wird.

Der uploadProgress Slot gibt mir recht schnell 26 von 26 aus, also müsste die Datei eigentlich da sein, ist sie aber nicht. Falls ihr euch wundert, warum ich die Klamotten tlw. auf den Stack, tlw. auf den Heap schiebe, dass liegt nur daran, dass ich im Zuge der Tests und Fehlersuche rumprobiert habe. Gibt es da einen Qt-Standard?

Das finished Signal des QNetworkReply gibt es wohl nicht? Zumindest sagt er dann immer etwas von QNetworkReplyImpl::finished() nicht gefunden.

Noch eine Frage zu QtNetworkAccessManager:

Wenn ich einen SslError bekomme, bspw. weil das Zertifikat abgelaufen ist und im Slot der Benutzer gefragt wird, ob dennoch fortgefahren werden soll, wie kommt man dann aus dem Slot wieder an die Stelle vor dem SslError emit? Das ist z.B. so ein Punkt, den ich bei Qt noch nicht verstanden habe. Wenn es da generell etwas gutes zu lesen gibt (bitte etwas anspruchsvoller als "da gibt signale und da slots und so kommunizieren die"), bitte einfach als Link mitgeben.

Ich danke allen, die hier helfen im Voraus,

MatthiasMit2T

Re: QtNetworkAccessManager

Verfasst: 11. Dezember 2009 08:36
von neuschi
MatthiasMit2T hat geschrieben: Ich bin Informatikstudent und gezwungen, nun mit Qt zu arbeiten.
Du Glücklicher! ;-)

Mal aus dem Bauch heraus, nicht getestet:

Code: Alles auswählen

 QNetworkAccessManager mgr;
 QNetworkReply *reply;

  reply = mgr.put( QNetworkRequest( this->url));
  
QEventLoop waiter;
 connect(  &mgr, SIGNAL(finished()), &waiter, SLOT(quit()));

  waiter.exec();
  qDebug() << reply->errorString();
Da ist natürlich noch kein Authentication-Handling, SSL Handling etc. drin. Nur als kleines Beispiel, wie das prinzipiell funktioniert.

Gruß, Ralf

Verfasst: 11. Dezember 2009 11:29
von MatthiasMit2T
Danke für die Idee, habe ich mal hinzugefügt.

Es scheint offensichtlich an der HTTP Authentifizierung zu liegen. Es gibt zwar keinen authorizeRequest - Slot aufruf, aber (laut Wireshark sniffing) wird zunächst ein 401 als reply gesendet. Da ich in der QUrl schon die Auth-Informationen drin habe, verstehe ich nicht ganz, weshalb er die nicht direkt mitsendet.

Beim zweiten Versuch, nach dem 401 Reply sendet er zwar die Header nochmal, offensichtlich aber nicht den Inhalt der Datei. Gibt es da Intern einen file->reset() oder kann ich das an irgendeiner Stelle selbst machen?

Ich vermute also, dass es daran liegt. Habe schon versucht, den Dateiinhalt erst in einen QBuffer zu schreiben und dann das QByteArray zu senden, aber das wird wohl auch nicht nochmal gesendet.

Danke für die Hilfe,
MatthiasMit2T

Verfasst: 12. Dezember 2009 01:16
von MatthiasMit2T
Also,

ich gehe fast davon aus, dass es an dem PUT Befehl ist. Dieser scheint offensichtlich das IODevice (in meinem Fall QFile) nach einem 401 Authorize vom Server nicht mit reset() zurück zu setzen. Bei einem normalen GET Request ist das kein Problem, dort wird einfach die Anfrage erneut gesendet, bei PUT jedoch nur die Requestheader, nicht jedoch der Body.

Nach der 4ten Ebene von QNetworkAccessManager::put habe ich bei QHttpNetworkConnection::queueRequest() aufgehört noch weiter zu gehen, weil es mir dann zu anstrengend wurde und ich das Gefühl hatte, diese Kette endet nie und ich verstehe eh grad nix mehr.

Falls jemand die QNetwork-Klassen mal so richtig durchgekaut hat und mir vielleicht erklären kann, weshalb der Body nicht erneut gesendet wird und, was ich dagegen tun kann, so melde er sich doch bitte :)

Solange alles gut funktioniert ist Qt ja wirklich nett, aber um selbst auf Fehlersuche zu gehen ist es echt eine Spur zu abstrakt.

Ich danke allen im Voraus.

MatthiasMit2T

QNetworkAccessManager

Verfasst: 12. Dezember 2009 07:44
von sowas
hallo,
ich habe in google "QNetworkAccessManager put" eingegeben und auf folgende Seite gestoßen, vll hilft sie dir weiter.

http://www.qtcentre.org/forum/f-qt-prog ... 17564.html

Verfasst: 14. Dezember 2009 10:52
von MatthiasMit2T
Hallo,

leider hilft mir das nicht weiter. Wie oben bereits beschrieben tritt das Problem nur dann auf, wenn der Server mit einem Authentication-Request antwortet. Ist der Upload ohne Authentication möglich, dann funktioniert er auch.

Leider wird das Signal authenticationRequired nicht emittet, auch dann nicht, wenn ich die Credentials nicht zuvor der Url hinzugefügt habe. Bis hierhin hatte ich überlegt, in dem entsprechenden Slot die Datei einfach selbst zu reset()-en, das fällt aus diesem Grund flach.

Hat nicht noch jemand eine Idee, wie sich das Problem lösen lässt?