QNetworkAccessManager never emits finished() signa

2019-07-27 10:31发布

问题:

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.

回答1:

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.