From d543c61b5a4ec8c4ee0c33bd024306d6376eaccd 2018-05-02 13:27:59 From: Alexis Jeandet Date: 2018-05-02 13:27:59 Subject: [PATCH] Basic Async implementation of downloader Signed-off-by: Alexis Jeandet --- diff --git a/core/include/Network/Downloader.h b/core/include/Network/Downloader.h index d2f4e5b..82c256d 100644 --- a/core/include/Network/Downloader.h +++ b/core/include/Network/Downloader.h @@ -10,14 +10,16 @@ #include #include +#include /** * @brief The Downloader handles all data donwloads in SciQLOP. */ class SCIQLOP_CORE_EXPORT Downloader{ public: - static Response get(const QString& url); - static Response get(const QString& url, const QString& user, const QString& passwd); + static Response get(const QString& url, const QString& user="", const QString& passwd=""); + static QUuid getAsync(const QString& url, std::function callback, const QString& user="", const QString& passwd=""); + static bool downloadFinished(QUuid uuid); static Downloader& instance() { diff --git a/core/src/Network/Downloader.cpp b/core/src/Network/Downloader.cpp index 20b85a5..6f285df 100644 --- a/core/src/Network/Downloader.cpp +++ b/core/src/Network/Downloader.cpp @@ -9,12 +9,30 @@ #include #include #include +#include class Downloader::p_Downloader { using login_pair=QPair; QNetworkAccessManager manager; QHash auth; + QReadWriteLock pending_requests_lock; + QHash pending_requests; + + QNetworkRequest buildRequest(const QString& url, const QString &user="", const QString &passwd="") + { + QNetworkRequest request; + request.setUrl(QUrl(url)); + request.setRawHeader("User-Agent", "SciQLop 1.0"); + if(user!="" and passwd!="") + { + //might grow quickly since we can have tons of URLs for the same host + auth[url]=login_pair(user,passwd); + QString login = "Basic "+user+":"+passwd; + request.setRawHeader("Authorization",login.toLocal8Bit()); + } + return request; + } public: explicit p_Downloader() @@ -29,37 +47,70 @@ public: authenticator->setPassword(login.second); } }; + QObject::connect(&manager, &QNetworkAccessManager::authenticationRequired, login_bambda); } Response get(const QString& url, const QString &user="", const QString &passwd="") { - QNetworkRequest request; - request.setUrl(QUrl(url)); - request.setRawHeader("User-Agent", "SciQLop 1.0"); - if(user!="" and passwd!="") - { - //might grow quickly since we can have tons of URLs for the same host - auth[url]=login_pair(user,passwd); - QString login = "Basic "+user+":"+passwd; - request.setRawHeader("Authorization",login.toLocal8Bit()); - } + QNetworkRequest request = buildRequest(url, user, passwd); QNetworkReply *reply = manager.get(request); while (!reply->isFinished()) QCoreApplication::processEvents(); QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - return Response(reply->readAll(), status_code.toInt()); + Response resp = Response(reply->readAll(), status_code.toInt()); + delete reply; + return resp; + } + + QUuid getAsync(const QString &url, std::function callback, const QString &user, const QString &passwd) + { + auto uuid = QUuid::createUuid(); + QNetworkRequest request = buildRequest(url, user, passwd); + QNetworkReply *reply = manager.get(request); + auto callback_wrapper = [uuid, callback, this](){ + QNetworkReply* reply; + { + QWriteLocker locker(&pending_requests_lock); + reply = pending_requests.take(uuid); + } + QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + Response resp = Response(reply->readAll(), status_code.toInt()); + delete reply; + callback(uuid, resp); + }; + QObject::connect(reply, &QNetworkReply::finished, callback_wrapper); + { + QWriteLocker locker(&pending_requests_lock); + pending_requests[uuid] = reply; + } + return uuid; + } + bool downloadFinished(QUuid uuid) + { + QReadLocker locker(&pending_requests_lock); + if(pending_requests.contains(uuid)) + { + auto req = pending_requests[uuid]; + return req->isFinished(); + } + return true; } }; -Response Downloader::get(const QString &url) +Response Downloader::get(const QString &url, const QString &user, const QString &passwd) { - return Downloader::instance().impl->get(url); + return Downloader::instance().impl->get(url, user, passwd); } -Response Downloader::get(const QString &url, const QString &user, const QString &passwd) +QUuid Downloader::getAsync(const QString &url, std::function callback, const QString &user, const QString &passwd) { - return Downloader::instance().impl->get(url, user, passwd); + return Downloader::instance().impl->getAsync(url, callback, user, passwd); +} + +bool Downloader::downloadFinished(QUuid uuid) +{ + return Downloader::instance().impl->downloadFinished(uuid); } Downloader::Downloader() diff --git a/core/tests/Network/TestDownloader.cpp b/core/tests/Network/TestDownloader.cpp index 3441210..9addf2a 100644 --- a/core/tests/Network/TestDownloader.cpp +++ b/core/tests/Network/TestDownloader.cpp @@ -22,6 +22,29 @@ private slots: QCOMPARE(resp.data(), QString("{\n \"user-agent\": \"SciQLop 1.0\"\n}\n")); } + void simpleAsyncGet() + { + bool done = false; + int status = -1; + QByteArray data; + auto callback = [&done, &status, &data](QUuid uuid,Response resp) + { + status = resp.status_code(); + done = true; + data = resp.data(); + }; + auto uuid = Downloader::getAsync("http://ovh.net/files/1Mio.dat", callback); + QCOMPARE(Downloader::downloadFinished(uuid), false); + while (!done) + { + QCoreApplication::processEvents(); + } + QCOMPARE(Downloader::downloadFinished(uuid), true); + QCOMPARE(status, 200); + QCOMPARE(data[0],'\xBA'); + QCOMPARE(data[data.length()-1],'\x20'); + } + void wrongUrl() { auto resp = Downloader::get("https://lpp.polytechniqe2.fr");