QT NetworkManager initialazation and asynch problems
-
Hello there, glorious people of the Qt brotherhood!
Firstly, I'd like to trigger an initialization process whenever this class been created. (DNS lookups for known hosts, and and pre-handshake with hosts over a specified number of slots - this shall not be hardcoded once it has been tested) - while all the classes are being created, config is being read, etc.
Secondly, I'd like to allow parallelized request sending depending on the allowed (and opened) slots present for the specific host.
Quite frankly there's multiple issues with the following code;
- Google seems to redirect, upon multiple requests at the same time to this host seems to result in a catastrophic failure, when x number or request triggers the finished signal, and that signals invokes my redirect checking and the first request get redirected, and I do seem to get the first reply back, with x error messages.
- I cant seem to invoke deleteLater() from httpFinished for the same reason
- I've could not find a way to wait for the slot number of request to be finished to start the next request in queue
- The initialization process does not finish before the first request been invoked, and therefore the init() and the sendGet() are running at the same time.
The main goal would be to gather data from multiple APIs using a single network class, those requests would maybe occur the same time, may not.
Please note that the code is just test of concept and for testing purposes only, and this shall be cleaned after it has been fixed, this is not how I intended it to work at all.
QT += core network QT -= gui TARGET = nebula CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp \ network.cpp HEADERS += \ network.h
network.h
#ifndef NETWORK_H #define NETWORK_H #include <QObject> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkReply> #include <QtNetwork/QHostInfo> #include <QtNetwork/QSslConfiguration> #include <QEventLoop> class network : public QObject { Q_OBJECT public: explicit network(QObject *parent = 0); ~network(); void sendGet(const QUrl &url); private: QNetworkAccessManager networkAccessManager; QHash<QString, QByteArray> usedSession; QNetworkReply *reply; QUrl url; Q_INVOKABLE void init(); void replyFinished(QUrl &url, QNetworkReply *reply); QList<QString> provideTcpHosts(); QList<QString> provideSslHosts(); private slots: void httpError(); void sslErrors(const QList<QSslError> &errors); void httpFinished(); }; #endif // NETWORK_H
network.cpp
#include "network.h" /** * @brief network::network * initialazing pre-networking initialization once the eventpool is ready * @param parent */ network::network(QObject *parent) : QObject(parent) { QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } network::~network() { networkAccessManager.deleteLater(); reply->deleteLater(); } /** * @brief network::init * dns chache warming for all hosts and pre-connecting to all hosts via 4 sockets */ void network::init() { QList<QString> tcpHosts = provideTcpHosts(); QList<QString> sslHosts = provideSslHosts(); // tcp hosts initialazation for( int i=0; i<tcpHosts.count(); ++i ) { // pre-dns lookup cache warming QHostInfo::lookupHost(tcpHosts[i], 0, 0); qDebug() << "pre-dns lookup cache warming for: " + tcpHosts[i]; // tcp pre-handshake with known hosts over 4 sockets with known http hosts for(int a=0; a<4; ++a) { networkAccessManager.connectToHost(tcpHosts[i], 80); qDebug() << "connecting " + QString::number(a+1) + "th socket for " + tcpHosts[i]; } } // tcp hosts initialazation for( int i=0; i<sslHosts.count(); ++i ) { // pre-dns lookup cache warming QHostInfo::lookupHost(sslHosts[i], 0, 0); qDebug() << "pre-dns lookup cache warming for: " + sslHosts[i]; // tcp pre-handshake with known hosts over 4 sockets with known http hosts for(int a=0; a<4; ++a) { networkAccessManager.connectToHostEncrypted(sslHosts[i], 443); qDebug() << "connecting " + QString::number(a+1) + "th socket for " + sslHosts[i]; } } } /** * @brief network::replyFinished * storing previous ssl session tickets for re-use, and error handling for finished requests * @param url * @param reply */ void network::replyFinished(QUrl &url, QNetworkReply *reply) { if(!usedSession.contains(url.toString())) { usedSession.insert(url.toString(), (QByteArray)reply->sslConfiguration().sessionTicket()); qDebug() << "saved ssl session ticket for" + url.toString(); } } /** * @brief network::sendGet * sending a simple GET request to specified url * @param url */ void network::sendGet(const QUrl &url) { qDebug() << "Sending a GET request to" + url.toString(); QNetworkRequest request; request.setUrl(url); request.setRawHeader("User-Agent", "nebula"); // reusing an ssl session ticket if exists for url if(usedSession.contains(url.toString())) { QSslConfiguration qssl; qssl.setSslOption(QSsl::SslOptionDisableSessionPersistence, false); qssl.setSessionTicket(usedSession.value(url.toString())); request.setSslConfiguration(qssl); qDebug() << "used ssl session ticket for" + url.toString(); } reply = networkAccessManager.get(request); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(httpError())); connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>))); connect(reply, SIGNAL(finished()), this, SLOT(httpFinished())); } /** * @brief network::provideTcpHosts * @return */ QList<QString> network::provideTcpHosts() { return QList<QString>() << "http://www.google.com" << "http://www.bing.com"; } /** * @brief network::provideSslHosts * @return */ QList<QString> network::provideSslHosts() { return QList<QString>() << "https://www.ssllabs.com/ssltest/"; } /*SLOTS*/ /** * @brief network::slotTcpError * @param tcpError */ void network::httpError() { qDebug() << "QNetworkRequest::HttpStatusCodeAttribute " << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute ) << "received"; qDebug() << reply->errorString(); } /** * @brief network::slotSslErrors * @param sslErrors */ void network::sslErrors(const QList<QSslError> &errors) { QString errorString; foreach (const QSslError &error, errors) { if (!errorString.isEmpty()) errorString += ", "; errorString += error.errorString(); } qDebug() << "ssl error recieved: " + errorString; } void network::httpFinished() { // possible redirect url QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (!redirectionTarget.isNull()) { this->sendGet(reply->url().resolved(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl())); } else { qDebug() << "QNetworkRequest::HttpStatusCodeAttribute " << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute ) << "received "; qDebug() << reply->errorString(); qDebug() << reply->readAll(); } // reply->deleteLater(); }
main.cpp
#include <QCoreApplication> #include <network.h> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); network networkAccessManager; for(int i = 0; i < 50; ++i) { networkAccessManager.sendGet((QString)"http://www.google.com"); } return a.exec(); }
Please, note I'm quite new to advanced C++ projects and to QT, this project partially -but will be used for my awesome project once its un-screwed- is to help me being familiarized with those topics.
Any help would be greatly appreciated!