diff options
Diffstat (limited to 'api/logic/net')
-rw-r--r-- | api/logic/net/Download.cpp | 74 | ||||
-rw-r--r-- | api/logic/net/Download.h | 3 | ||||
-rw-r--r-- | api/logic/net/HttpMetaCache.cpp | 2 | ||||
-rw-r--r-- | api/logic/net/HttpMetaCache.h | 2 | ||||
-rw-r--r-- | api/logic/net/Mode.h | 10 | ||||
-rw-r--r-- | api/logic/net/NetAction.h | 73 | ||||
-rw-r--r-- | api/logic/net/NetJob.cpp | 84 | ||||
-rw-r--r-- | api/logic/net/NetJob.h | 41 | ||||
-rw-r--r-- | api/logic/net/PasteUpload.cpp | 43 | ||||
-rw-r--r-- | api/logic/net/PasteUpload.h | 4 | ||||
-rw-r--r-- | api/logic/net/URLConstants.h | 2 |
11 files changed, 207 insertions, 131 deletions
diff --git a/api/logic/net/Download.cpp b/api/logic/net/Download.cpp index 12c1b201..631d3076 100644 --- a/api/logic/net/Download.cpp +++ b/api/logic/net/Download.cpp @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -102,7 +102,8 @@ void Download::start() connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); - connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); + connect(rep, &QNetworkReply::sslErrors, this, &Download::sslErrors); + connect(rep, &QNetworkReply::readyRead, this, &Download::downloadReadyRead); } void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) @@ -135,31 +136,68 @@ void Download::downloadError(QNetworkReply::NetworkError error) } } -bool Download::handleRedirect() +void Download::sslErrors(const QList<QSslError> & errors) { - QVariant redirect = m_reply->header(QNetworkRequest::LocationHeader); - QString redirectURL; - if(redirect.isValid()) + int i = 1; + for (auto error : errors) { - redirectURL = redirect.toString(); + qCritical() << "Download" << m_url.toString() << "SSL Error #" << i << " : " << error.errorString(); + auto cert = error.certificate(); + qCritical() << "Certificate in question:\n" << cert.toText(); + i++; } - // FIXME: This is a hack for https://bugreports.qt-project.org/browse/QTBUG-41061 - else if(m_reply->hasRawHeader("Location")) +} + +bool Download::handleRedirect() +{ + QUrl redirect = m_reply->header(QNetworkRequest::LocationHeader).toUrl(); + if(!redirect.isValid()) { - auto data = m_reply->rawHeader("Location"); - if(data.size() > 2 && data[0] == '/' && data[1] == '/') + if(!m_reply->hasRawHeader("Location")) + { + // no redirect -> it's fine to continue + return false; + } + // there is a Location header, but it's not correct. we need to apply some workarounds... + QByteArray redirectBA = m_reply->rawHeader("Location"); + if(redirectBA.size() == 0) + { + // empty, yet present redirect header? WTF? + return false; + } + QString redirectStr = QString::fromUtf8(redirectBA); + + /* + * IF the URL begins with //, we need to insert the URL scheme. + * See: https://bugreports.qt-project.org/browse/QTBUG-41061 + */ + if(redirectStr.startsWith("//")) + { + redirectStr = m_reply->url().scheme() + ":" + redirectStr; + } + + /* + * Next, make sure the URL is parsed in tolerant mode. Qt doesn't parse the location header in tolerant mode, which causes issues. + * FIXME: report Qt bug for this + */ + redirect = QUrl(redirectStr, QUrl::TolerantMode); + if(!redirect.isValid()) { - redirectURL = m_reply->url().scheme() + ":" + data; + qWarning() << "Failed to parse redirect URL:" << redirectStr; + downloadError(QNetworkReply::ProtocolFailure); + return false; } + qDebug() << "Fixed location header:" << redirect; } - if (!redirectURL.isEmpty()) + else { - m_url = QUrl(redirect.toString()); - qDebug() << "Following redirect to " << m_url.toString(); - start(); - return true; + qDebug() << "Location header:" << redirect; } - return false; + + m_url = QUrl(redirect.toString()); + qDebug() << "Following redirect to " << m_url.toString(); + start(); + return true; } diff --git a/api/logic/net/Download.h b/api/logic/net/Download.h index 3347dc96..00bf108c 100644 --- a/api/logic/net/Download.h +++ b/api/logic/net/Download.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,6 +58,7 @@ private: /* methods */ protected slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override; void downloadError(QNetworkReply::NetworkError error) override; + void sslErrors(const QList<QSslError> & errors); void downloadFinished() override; void downloadReadyRead() override; diff --git a/api/logic/net/HttpMetaCache.cpp b/api/logic/net/HttpMetaCache.cpp index 3eec185b..ebcb0a27 100644 --- a/api/logic/net/HttpMetaCache.cpp +++ b/api/logic/net/HttpMetaCache.cpp @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/api/logic/net/HttpMetaCache.h b/api/logic/net/HttpMetaCache.h index 21c9b6c8..7ee5f735 100644 --- a/api/logic/net/HttpMetaCache.h +++ b/api/logic/net/HttpMetaCache.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/api/logic/net/Mode.h b/api/logic/net/Mode.h new file mode 100644 index 00000000..62e26d92 --- /dev/null +++ b/api/logic/net/Mode.h @@ -0,0 +1,10 @@ +#pragma once + +namespace Net +{ +enum class Mode +{ + Offline, + Online +}; +} diff --git a/api/logic/net/NetAction.h b/api/logic/net/NetAction.h index a533c317..08e40a29 100644 --- a/api/logic/net/NetAction.h +++ b/api/logic/net/NetAction.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,10 @@ enum JobStatus Job_Finished, Job_Failed, Job_Aborted, + /* + * FIXME: @NUKE this confuses the task failing with us having a fallback in the form of local data. Clear up the confusion. + * Same could be true for aborted task - the presence of pre-existing result is a separate concern + */ Job_Failed_Proceed }; @@ -43,18 +47,26 @@ protected: public: virtual ~NetAction() {}; -public: - virtual qint64 totalProgress() const + bool isRunning() const { - return m_total_progress; + return m_status == Job_InProgress; } - virtual qint64 currentProgress() const + bool isFinished() const { - return m_progress; + return m_status >= Job_Finished; } - virtual qint64 numberOfFailures() const + bool wasSuccessful() const { - return m_failures; + return m_status == Job_Finished || m_status == Job_Failed_Proceed; + } + + qint64 totalProgress() const + { + return m_total_progress; + } + qint64 currentProgress() const + { + return m_progress; } virtual bool abort() { @@ -64,25 +76,10 @@ public: { return false; } - -public: - /// the network reply - unique_qobject_ptr<QNetworkReply> m_reply; - - /// source URL - QUrl m_url; - - /// The file's status - JobStatus m_status = Job_NotStarted; - - /// index within the parent job - int m_index_within_job = 0; - - qint64 m_progress = 0; - qint64 m_total_progress = 1; - - /// number of failures up to this point - int m_failures = 0; + QUrl url() + { + return m_url; + } signals: void started(int index); @@ -91,14 +88,28 @@ signals: void failed(int index); void aborted(int index); -protected -slots: +protected slots: virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; -public -slots: +public slots: virtual void start() = 0; + +public: + /// index within the parent job, FIXME: nuke + int m_index_within_job = 0; + + /// the network reply + unique_qobject_ptr<QNetworkReply> m_reply; + + /// source URL + QUrl m_url; + + qint64 m_progress = 0; + qint64 m_total_progress = 1; + +protected: + JobStatus m_status = Job_NotStarted; }; diff --git a/api/logic/net/NetJob.cpp b/api/logic/net/NetJob.cpp index 275da749..304b5820 100644 --- a/api/logic/net/NetJob.cpp +++ b/api/logic/net/NetJob.cpp @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,55 +59,74 @@ void NetJob::partAborted(int index) void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) { auto &slot = parts_progress[index]; - - current_progress -= slot.current_progress; slot.current_progress = bytesReceived; - current_progress += slot.current_progress; - - total_progress -= slot.total_progress; slot.total_progress = bytesTotal; - total_progress += slot.total_progress; - setProgress(current_progress, total_progress); + + int done = m_done.size(); + int doing = m_doing.size(); + int all = parts_progress.size(); + + qint64 bytesAll = 0; + qint64 bytesTotalAll = 0; + for(auto & partIdx: m_doing) + { + auto part = parts_progress[partIdx]; + // do not count parts with unknown/nonsensical total size + if(part.total_progress <= 0) + { + continue; + } + bytesAll += part.current_progress; + bytesTotalAll += part.total_progress; + } + + qint64 inprogress = (bytesTotalAll == 0) ? 0 : (bytesAll * 1000) / bytesTotalAll; + auto current = done * 1000 + doing * inprogress; + auto current_total = all * 1000; + // HACK: make sure it never jumps backwards. + if(m_current_progress > current) + { + current = m_current_progress; + } + m_current_progress = current; + setProgress(current, current_total); } void NetJob::executeTask() { - qDebug() << m_job_name.toLocal8Bit() << " started."; - m_running = true; - for (int i = 0; i < downloads.size(); i++) - { - m_todo.enqueue(i); - } // hack that delays early failures so they can be caught easier QMetaObject::invokeMethod(this, "startMoreParts", Qt::QueuedConnection); } void NetJob::startMoreParts() { - // check for final conditions if there's nothing in the queue + if(!isRunning()) + { + // this actually makes sense. You can put running downloads into a NetJob and then not start it until much later. + return; + } + // OK. We are actively processing tasks, proceed. + // Check for final conditions if there's nothing in the queue. if(!m_todo.size()) { if(!m_doing.size()) { if(!m_failed.size()) { - qDebug() << m_job_name << "succeeded."; emitSucceeded(); } else if(m_aborted) { - qDebug() << m_job_name << "aborted."; - emitFailed(tr("Job '%1' aborted.").arg(m_job_name)); + emitAborted(); } else { - qCritical() << m_job_name << "failed."; - emitFailed(tr("Job '%1' failed to process:\n%2").arg(m_job_name).arg(getFailedFiles().join("\n"))); + emitFailed(tr("Job '%1' failed to process:\n%2").arg(objectName()).arg(getFailedFiles().join("\n"))); } } return; } - // otherwise try to start more parts + // There's work to do, try to start more parts. while (m_doing.size() < 6) { if(!m_todo.size()) @@ -131,7 +150,7 @@ QStringList NetJob::getFailedFiles() QStringList failed; for (auto index: m_failed) { - failed.push_back(downloads[index]->m_url.toString()); + failed.push_back(downloads[index]->url().toString()); } failed.sort(); return failed; @@ -170,3 +189,24 @@ bool NetJob::abort() } return fullyAborted; } + +bool NetJob::addNetAction(NetActionPtr action) +{ + action->m_index_within_job = downloads.size(); + downloads.append(action); + part_info pi; + parts_progress.append(pi); + partProgress(parts_progress.count() - 1, action->currentProgress(), action->totalProgress()); + + if(action->isRunning()) + { + connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); + connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64))); + } + else + { + m_todo.append(parts_progress.size() - 1); + } + return true; +} diff --git a/api/logic/net/NetJob.h b/api/logic/net/NetJob.h index ca4f5df1..be58c61a 100644 --- a/api/logic/net/NetJob.h +++ b/api/logic/net/NetJob.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,32 +30,13 @@ class MULTIMC_LOGIC_EXPORT NetJob : public Task { Q_OBJECT public: - explicit NetJob(QString job_name) : Task(), m_job_name(job_name) {} - virtual ~NetJob() {} - bool addNetAction(NetActionPtr action) + explicit NetJob(QString job_name) : Task() { - action->m_index_within_job = downloads.size(); - downloads.append(action); - part_info pi; - { - pi.current_progress = action->currentProgress(); - pi.total_progress = action->totalProgress(); - pi.failures = action->numberOfFailures(); - } - parts_progress.append(pi); - total_progress += pi.total_progress; - // if this is already running, the action needs to be started right away! - if (isRunning()) - { - setProgress(current_progress, total_progress); - connect(action.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); - connect(action.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(action.get(), SIGNAL(netActionProgress(int, qint64, qint64)), - SLOT(partProgress(int, qint64, qint64))); - action->start(); - } - return true; + setObjectName(job_name); } + virtual ~NetJob() {} + + bool addNetAction(NetActionPtr action); NetActionPtr operator[](int index) { @@ -75,10 +56,6 @@ public: { return downloads.size(); } - virtual bool isRunning() const override - { - return m_running; - } QStringList getFailedFiles(); bool canAbort() const override; @@ -102,17 +79,13 @@ private: qint64 current_progress = 0; qint64 total_progress = 1; int failures = 0; - bool connected = false; }; - QString m_job_name; QList<NetActionPtr> downloads; QList<part_info> parts_progress; QQueue<int> m_todo; QSet<int> m_doing; QSet<int> m_done; QSet<int> m_failed; - qint64 current_progress = 0; - qint64 total_progress = 0; - bool m_running = false; + qint64 m_current_progress = 0; bool m_aborted = false; }; diff --git a/api/logic/net/PasteUpload.cpp b/api/logic/net/PasteUpload.cpp index 59779b2c..d1ddf39d 100644 --- a/api/logic/net/PasteUpload.cpp +++ b/api/logic/net/PasteUpload.cpp @@ -2,41 +2,45 @@ #include "Env.h" #include <QDebug> #include <QJsonObject> +#include <QJsonArray> #include <QJsonDocument> +#include <QFile> PasteUpload::PasteUpload(QWidget *window, QString text, QString key) : m_window(window) { m_key = key; QByteArray temp; - temp = text.toUtf8(); - temp.replace('\n', "\r\n"); - m_textSize = temp.size(); - m_text = "key=" + m_key.toLatin1() + "&description=MultiMC5+Log+File&language=plain&format=json&expire=2592000&paste=" + temp.toPercentEncoding(); - buf = new QBuffer(&m_text); + QJsonObject topLevelObj; + QJsonObject sectionObject; + sectionObject.insert("contents", text); + QJsonArray sectionArray; + sectionArray.append(sectionObject); + topLevelObj.insert("description", "MultiMC Log Upload"); + topLevelObj.insert("sections", sectionArray); + QJsonDocument docOut; + docOut.setObject(topLevelObj); + m_jsonContent = docOut.toJson(); } PasteUpload::~PasteUpload() { - if(buf) - { - delete buf; - } } bool PasteUpload::validateText() { - return m_textSize <= maxSize(); + return m_jsonContent.size() <= maxSize(); } void PasteUpload::executeTask() { - QNetworkRequest request(QUrl("https://paste.ee/api")); + QNetworkRequest request(QUrl("https://api.paste.ee/v1/pastes")); request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); - request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); - request.setRawHeader("Content-Length", QByteArray::number(m_text.size())); + request.setRawHeader("Content-Type", "application/json"); + request.setRawHeader("Content-Length", QByteArray::number(m_jsonContent.size())); + request.setRawHeader("X-Auth-Token", m_key.toStdString().c_str()); - QNetworkReply *rep = ENV.qnam().post(request, buf); + QNetworkReply *rep = ENV.qnam().post(request, m_jsonContent); m_reply = std::shared_ptr<QNetworkReply>(rep); setStatus(tr("Uploading to paste.ee")); @@ -54,10 +58,10 @@ void PasteUpload::downloadError(QNetworkReply::NetworkError error) void PasteUpload::downloadFinished() { + QByteArray data = m_reply->readAll(); // if the download succeeded if (m_reply->error() == QNetworkReply::NetworkError::NoError) { - QByteArray data = m_reply->readAll(); m_reply.reset(); QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); @@ -85,14 +89,15 @@ void PasteUpload::downloadFinished() bool PasteUpload::parseResult(QJsonDocument doc) { auto object = doc.object(); - auto status = object.value("status").toString("error"); - if (status == "error") + auto status = object.value("success").toBool(); + if (!status) { qCritical() << "paste.ee reported error:" << QString(object.value("error").toString()); return false; } - m_pasteLink = object.value("paste").toObject().value("link").toString(); - m_pasteID = object.value("paste").toObject().value("id").toString(); + m_pasteLink = object.value("link").toString(); + m_pasteID = object.value("id").toString(); + qDebug() << m_pasteLink; return true; } diff --git a/api/logic/net/PasteUpload.h b/api/logic/net/PasteUpload.h index 78d1da8e..62d2e766 100644 --- a/api/logic/net/PasteUpload.h +++ b/api/logic/net/PasteUpload.h @@ -34,14 +34,12 @@ protected: private: bool parseResult(QJsonDocument doc); - QByteArray m_text; QString m_error; QWidget *m_window; QString m_pasteID; QString m_pasteLink; QString m_key; - int m_textSize = 0; - QBuffer * buf = nullptr; + QByteArray m_jsonContent; std::shared_ptr<QNetworkReply> m_reply; public slots: diff --git a/api/logic/net/URLConstants.h b/api/logic/net/URLConstants.h index dedb1424..22d128b2 100644 --- a/api/logic/net/URLConstants.h +++ b/api/logic/net/URLConstants.h @@ -1,4 +1,4 @@ -/* Copyright 2013-2017 MultiMC Contributors +/* Copyright 2013-2018 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. |