Search code examples
c++qtqnetworkaccessmanager

QNetworkReply and 301 redirect


I have a webviewer and only want it to only be able to access our webapps, to achieve this I have placed a php header which I look for in my Qt App. This works fine but with one exception and that's with 301 permanent moved status codes. Modern browsers redirect you automatically but putting a "/" at the end of the http request.

When a URL to our web app is entered it currently needs the trailing slash to be able to detect the headers but I want it to also get at that header even if they don't put a trailing slash.

Here is my current method to retrieve the header:

QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QNetworkRequest request;
    request.setUrl(url);
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*)));

    request.setRawHeader("User-Agent", "CytoViewer 1.0");
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/CytoViewer");
    QNetworkReply *reply = manager->get(request);
    reply->ignoreSslErrors();
    QEventLoop loop;

    connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
    loop.exec();
    qDebug() << "QLoop reply all: " << reply->readAll();
    qDebug() << "QLoop: " << reply->rawHeader("Cyto-Study-Manager");
    if(reply->rawHeader("OurWebApp") == "1"){
        //Header exists?(QEventLoop finish) Set arg[1]"url 'Found prouct: product header'"
        product = reply->rawHeader("Product");
        return true;
    } else {
        //Header doen't exist? Graceful error - not a valid PI product
        return false;
    }

To solve the problem of of hitting a 301 first I send two network requests, The first one hits the URL entered and check for a 301 if there is a 301 status code it get's the proposed url via the reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); method and returns that URL else if there is no status code then it simply returns the previous URL the user entered and then sends another network request to check the headers.

First request I send out to check status code:

QUrl MainWindow::networkRequest(QUrl checkUrl){
    qDebug() << "checkURL: " << checkUrl;
    //
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QNetworkRequest request;
    request.setUrl(checkUrl);
    request.setRawHeader("User-Agent", "CytoViewer 1.0");
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/CytoViewer");
    QNetworkReply *reply = manager->get(request);
    reply->ignoreSslErrors();
    QEventLoop checkLoop;
    connect(reply, SIGNAL(finished()), &checkLoop, SLOT(quit()));
    checkLoop.exec();
    //Check status code
    if (reply->error() == QNetworkReply::NoError) {
        int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        if(statusCode == 301) {
            QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
            return redirectUrl;
        }else {
            return checkUrl;
        }

    }
}

Long story short, I am sending two network requests, 1) To check for 301 2) To check for our app header.

Is there anyway to do this in one request? Am I missing a method that will do this redirection automatically?

Regards

Nathan


Solution

  • Apparently there is not.

    There's an official HOWTO entry on https://web.archive.org/web/20141101060340/http://developer.nokia.com/community/wiki/Handling_an_HTTP_redirect_with_QNetworkAccessManager

    Extraction from link above:

    void QNAMRedirect::replyFinished(QNetworkReply* reply) {
        /*
         * Reply is finished!
         * We'll ask for the reply about the Redirection attribute
         * http://doc.trolltech.com/qnetworkrequest.html#Attribute-enum
         */
        QVariant possibleRedirectUrl =
                 reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
     
        /* We'll deduct if the redirection is valid in the redirectUrl function */
        _urlRedirectedTo = this->redirectUrl(possibleRedirectUrl.toUrl(),
                                             _urlRedirectedTo);
     
        /* If the URL is not empty, we're being redirected. */
        if(!_urlRedirectedTo.isEmpty()) {
            QString text = QString("QNAMRedirect::replyFinished: Redirected to ")
                                  .append(_urlRedirectedTo.toString());
            this->_textContainer->setText(text);
     
            /* We'll do another request to the redirection url. */
            this->_qnam->get(QNetworkRequest(_urlRedirectedTo));
        }
        else {
            /*
             * We weren't redirected anymore
             * so we arrived to the final destination...
             */
            QString text = QString("QNAMRedirect::replyFinished: Arrived to ")
                                  .append(reply->url().toString());
            this->_textContainer->setText(text);
            /* ...so this can be cleared. */
            _urlRedirectedTo.clear();
        }
        /* Clean up. */
        reply->deleteLater();
    }
     
    QUrl QNAMRedirect::redirectUrl(const QUrl& possibleRedirectUrl,
                                   const QUrl& oldRedirectUrl) const {
        QUrl redirectUrl;
        /*
         * Check if the URL is empty and
         * that we aren't being fooled into a infinite redirect loop.
         * We could also keep track of how many redirects we have been to
         * and set a limit to it, but we'll leave that to you.
         */
        if(!possibleRedirectUrl.isEmpty() &&
           possibleRedirectUrl != oldRedirectUrl) {
            redirectUrl = possibleRedirectUrl;
        }
        return redirectUrl;
    }