1

I'm working on a module for a project where HTTP GET requests are used to retrieve some XML data, which is then converted to another format and send to a sub-system.

The code I have written so far is below:

CMakeLists.txt:

project(HttpDemo)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)

#find_package(Qt5Widgets)
find_package(Qt5Core)
find_package(Qt5Network)

set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
qt5_use_modules(${PROJECT_NAME} Core Network) #Gui Widgets

main.cpp

#include <QtCore>
#include <QtNetwork>

class HttpHandler : public QObject
{
    Q_OBJECT
public:
    HttpHandler(QObject* parent=Q_NULLPTR) : QObject(parent)
    {
        QObject::connect(&nm, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
        qDebug() << QSslSocket::sslLibraryBuildVersionString();
    }
private:
    QNetworkAccessManager nm;
public slots:
    void post(QString urlLink)
    {
        QUrl url(urlLink);
        QNetworkRequest request(url);
        QSslConfiguration sslConf;
        sslConf.setProtocol(QSsl::SslV3);
        request.setSslConfiguration(sslConf);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencded");

        QUrlQuery query;
        query.addQueryItem("client_id", "1234");
        query.addQueryItem("code", "abcd");

        QUrl params;
        params.setQuery(query);

        nm.post(request, params.toEncoded());
    }

    void get(QString urlLink)
    {
        QUrl url(urlLink);
        QNetworkRequest request(url);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

        nm.get(request);
    }

    void replyFinished(QNetworkReply* reply)
    {
        QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        if(statusCode.isValid())
        {
            // Print or catch the status code
            QString status = statusCode.toString(); // or status_code.toInt();
            qDebug() << status;
            qDebug() << QString::fromStdString(reply->readAll().toStdString());
        }
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    HttpHandler hh;
    hh.get("SOME_URL");

    return a.exec();
}

#include "main.moc"

With SOME_URL I have tried a lot of links all of which work without any issues in let's say the Http Requester addon for Firefox. I get:

"OpenSSL 1.0.1j 15 Oct 2014"

qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method

qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method

According to the authority called the Internet this shouldn't be a problem. One thing's for certain - my replyFinished(QNetworkReply*) slot doesn't get triggered although it is connected to the finished() signal of the QNetworkAccessManager. This means that whatever the reason the signal is not emitted. Changing the QSslConfiguration to a different QSsl::SslProtocol doesn't make a difference in the outcome.


UPDATE (as requested in the comments):

Following code uses readyRead() and dynamically allocated reply. The result - same as above.

#include <QtCore>
#include <QtNetwork>

class HttpHandler : public QObject
{
    Q_OBJECT
public:
    HttpHandler(QObject* parent=Q_NULLPTR) : QObject(parent)
    {
        qDebug() << QSslSocket::sslLibraryBuildVersionString();
        this->manager = new QNetworkAccessManager(this);
        this->reply = Q_NULLPTR;
    }
private:
    QNetworkAccessManager* manager;
    QNetworkReply* reply;
signals:
    void finished();
public slots:
    void post(QString urlLink)
    {
        QUrl url(urlLink);
        QNetworkRequest request(url);
        QSslConfiguration sslConf;
        sslConf.setProtocol(QSsl::SslV2);
        request.setSslConfiguration(sslConf);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

        QUrlQuery query;
        query.addQueryItem("client_id", "1234");
        query.addQueryItem("code", "abcd");

        QUrl params;
        params.setQuery(query);

        manager->post(request, params.toEncoded());
    }

    void get(QString urlLink)
    {
        QUrl url(urlLink);
        QNetworkRequest request(url);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");

        this->reply = manager->get(request);
        QObject::connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
    }

    void slotReadyRead()
    {
        qDebug() << "Hello"; // I never land here
    }

    void replyFinished(QNetworkReply* reply)
    {
        QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        if(statusCode.isValid())
        {
            QString status = statusCode.toString(); // or status_code.toInt();
            qDebug() << status;
            qDebug() << QString::fromStdString(reply->readAll().toStdString());
        }

        emit finished();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    HttpHandler *hh = new HttpHandler(&a);
    QObject::connect(hh, SIGNAL(finished()), &a, SLOT(quit()));

    hh->get("http://httpbin.org/ip"); // or any other httpbin.org endpoint

    return a.exec();
}

#include "main.moc"

UPDATE 2:

I just found an example in the Qt documentation. Downloaded, compiled and ran the thing - same error but it works.

rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
  • Can't it be a problem with your OpenSsl library, which is connected to Qt? – Alex Oct 12 '17 at 12:37
  • It is possible but I have no idea how to "debug" this problem. I've read that SSL library can be statically linked to Qt so that you can ship your own version but I'm not sure how much work this introduces to my work. – rbaleksandar Oct 12 '17 at 12:39
  • Can you try to look at `void sslErrors(QNetworkReply *reply, const QList &errors)`, maybe there you can find something? Or maybe ar `NetworkError QNetworkReply::error() const`? – Alex Oct 12 '17 at 12:46
  • I actually tried connecting the `sslErrors()` signal but it never got emitted. :-/ There is no point in calling `error()` since I don't even land in the `replyFinished()` slot (because the signal that will allow me to do that doesn't seem to be emitted). – rbaleksandar Oct 12 '17 at 12:54
  • Try to change your signal to readyRead(), to see whether any data is in, or none? – Alex Oct 12 '17 at 13:15
  • And maybe replece your QNRequest with dynamic one? And maybe not only it, but every other object, that is passed forwars by reference? https://stackoverflow.com/questions/7603833/what-happens-in-c-when-i-pass-an-object-by-reference-and-it-goes-out-of-scope – Alex Oct 12 '17 at 13:36
  • Done but no change. I even put the reply as a class member. – rbaleksandar Oct 12 '17 at 13:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/156566/discussion-between-alex-and-rbaleksandar). – Alex Oct 12 '17 at 14:01

1 Answers1

0

Issue resolved (see here). Basically the problem was the company's proxy. A colleague of mine gave it a shot and replaced the HTTP with HTTPS (even though the link is HTTP) and it worked all of a sudden. Then it struck us - the company's proxy caches HTTP (and does other things too), which leads to huge delays and if the timeout tolerance is small enough QNetworkAccessManager will return a socket timeout.

Using QNetworkProxyFactory::setUseSystemConfiguration(true) enables the proxy in a way that doesn't make your application dependent on a configuration in your code but rather the configuration of the system.

rbaleksandar
  • 8,713
  • 7
  • 76
  • 161
  • It's good to hear that you found the cause of your problem. You could make your answer more helpful for future readers by explaining *how* you diagnosed it, and in particular, which evidence will identify this problem when it's seen elsewhere. And please replace/augment that link with a summary description, so that your answer is complete in itself; thanks. – Toby Speight Oct 16 '17 at 17:36