Better abstraction for http multipart responses.

This commit is contained in:
Martin Rotter 2017-10-20 14:26:14 +02:00
parent e9c3c10f53
commit 134c25e86d
9 changed files with 829 additions and 747 deletions

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
#include "network-web/downloader.h"
#include "miscellaneous/iofactory.h"
#include "network-web/silentnetworkaccessmanager.h"
#include <QHttpMultiPart>
@ -12,7 +13,7 @@ Downloader::Downloader(QObject* parent)
: QObject(parent), m_activeReply(nullptr), m_downloadManager(new SilentNetworkAccessManager(this)),
m_timer(new QTimer(this)), m_customHeaders(QHash<QByteArray, QByteArray>()), m_inputData(QByteArray()),
m_inputMultipartData(nullptr), m_targetProtected(false), m_targetUsername(QString()), m_targetPassword(QString()),
m_lastOutputData(QByteArray()), m_lastOutputMultipartData(QList<QHttpPart*>()), m_lastOutputError(QNetworkReply::NoError),
m_lastOutputData(QByteArray()), m_lastOutputMultipartData(QList<HttpResponse>()), m_lastOutputError(QNetworkReply::NoError),
m_lastContentType(QVariant()) {
m_timer->setInterval(DOWNLOAD_TIMEOUT);
m_timer->setSingleShot(true);
@ -172,11 +173,11 @@ void Downloader::progressInternal(qint64 bytes_received, qint64 bytes_total) {
emit progress(bytes_received, bytes_total);
}
QList<QHttpPart*> Downloader::decodeMultipartAnswer(QNetworkReply* reply) {
QList<HttpResponse> Downloader::decodeMultipartAnswer(QNetworkReply* reply) {
QByteArray data = reply->readAll();
if (data.isEmpty()) {
return QList<QHttpPart*>();
return QList<HttpResponse>();
}
QString content_type = reply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toString();
@ -184,10 +185,34 @@ QList<QHttpPart*> Downloader::decodeMultipartAnswer(QNetworkReply* reply) {
QRegularExpression regex(QL1S("--") + boundary + QL1S("(--)?(\\r\\n)?"));
QStringList list = QString::fromUtf8(data).split(regex, QString::SplitBehavior::SkipEmptyParts);
QList<QHttpPart*> parts;
QList<HttpResponse> parts;
parts.reserve(list.size());
foreach (const QString& http_response_str, list) {
// We separate headers and body.
HttpResponse new_part;
int start_of_http = http_response_str.indexOf(QL1S("HTTP/1.1"));
int start_of_headers = http_response_str.indexOf(QRegularExpression(QSL("\\r\\r?\\n")), start_of_http);
int start_of_body = http_response_str.indexOf(QRegularExpression(QSL("(\\r\\r?\\n){2,}")), start_of_headers + 2);
QString body = http_response_str.mid(start_of_body);
QString headers = http_response_str.mid(start_of_headers,
start_of_body - start_of_headers).replace(QRegularExpression(QSL("[\\n\\r]+")),
QSL("\n"));
foreach (const QString& header_line, headers.split(QL1C('\n'), QString::SplitBehavior::SkipEmptyParts)) {
int index_colon = header_line.indexOf(QL1C(':'));
if (index_colon > 0) {
new_part.appendHeader(header_line.mid(0, index_colon),
header_line.mid(index_colon + 2));
}
}
new_part.setBody(body);
parts.append(new_part);
}
return parts;
}
@ -262,7 +287,7 @@ QNetworkReply::NetworkError Downloader::lastOutputError() const {
return m_lastOutputError;
}
QList<QHttpPart*> Downloader::lastOutputMultipartData() const {
QList<HttpResponse> Downloader::lastOutputMultipartData() const {
return m_lastOutputMultipartData;
}

View File

@ -6,13 +6,14 @@
#include <QObject>
#include "definitions/definitions.h"
#include "network-web/httpresponse.h"
#include <QHttpMultiPart>
#include <QNetworkReply>
#include <QSslError>
class SilentNetworkAccessManager;
class QTimer;
class QHttpPart;
class Downloader : public QObject {
Q_OBJECT
@ -26,7 +27,7 @@ class Downloader : public QObject {
// Access to last received full output data/error/content-type.
QByteArray lastOutputData() const;
QNetworkReply::NetworkError lastOutputError() const;
QList<QHttpPart*> lastOutputMultipartData() const;
QList<HttpResponse> lastOutputMultipartData() const;
QVariant lastContentType() const;
public slots:
@ -67,7 +68,7 @@ class Downloader : public QObject {
void progressInternal(qint64 bytes_received, qint64 bytes_total);
private:
QList<QHttpPart*> decodeMultipartAnswer(QNetworkReply* reply);
QList<HttpResponse> decodeMultipartAnswer(QNetworkReply* reply);
void manipulateData(const QString& url, QNetworkAccessManager::Operation operation,
const QByteArray& data, QHttpMultiPart* multipart_data,
int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false,
@ -94,7 +95,7 @@ class Downloader : public QObject {
// Response data.
QByteArray m_lastOutputData;
QList<QHttpPart*> m_lastOutputMultipartData;
QList<HttpResponse> m_lastOutputMultipartData;
QNetworkReply::NetworkError m_lastOutputError;
QVariant m_lastContentType;

View File

@ -0,0 +1,26 @@
// For license of this file, see <object-root-folder>/LICENSE.md.
#include "network-web/httpresponse.h"
HttpResponse::HttpResponse() : m_headers(QList<HttpHeader>()), m_body(QString()) {}
QString HttpResponse::body() const {
return m_body;
}
QList<HttpHeader> HttpResponse::headers() const {
return m_headers;
}
void HttpResponse::appendHeader(const QString& name, const QString& value) {
HttpHeader head;
head.first = name;
head.second = value;
m_headers.append(head);
}
void HttpResponse::setBody(const QString& body) {
m_body = body;
}

26
src/network-web/httpresponse.h Executable file
View File

@ -0,0 +1,26 @@
// For license of this file, see <object-root-folder>/LICENSE.md.
#ifndef HTTPRESPONSE_H
#define HTTPRESPONSE_H
#include <QList>
#include <QPair>
typedef QPair<QString, QString> HttpHeader;
class HttpResponse {
public:
explicit HttpResponse();
QString body() const;
void setBody(const QString& body);
QList<HttpHeader> headers() const;
void appendHeader(const QString& name, const QString& value);
private:
QList<HttpHeader> m_headers;
QString m_body;
};
#endif // HTTPRESPONSE_H

View File

@ -208,7 +208,7 @@ NetworkResult NetworkFactory::performNetworkOperation(const QString& url, int ti
NetworkResult NetworkFactory::performNetworkOperation(const QString& url,
int timeout,
QHttpMultiPart* input_data,
QList<QHttpPart*>& output,
QList<HttpResponse>& output,
QNetworkAccessManager::Operation operation,
QList<QPair<QByteArray, QByteArray>> additional_headers,
bool protected_contents,

View File

@ -3,6 +3,8 @@
#ifndef NETWORKFACTORY_H
#define NETWORKFACTORY_H
#include "network-web/httpresponse.h"
#include <QCoreApplication>
#include <QHttpPart>
#include <QNetworkReply>
@ -51,7 +53,7 @@ class NetworkFactory {
const QString& password = QString());
static NetworkResult performNetworkOperation(const QString& url, int timeout,
QHttpMultiPart* input_data,
QList<QHttpPart*>& output,
QList<HttpResponse>& output,
QNetworkAccessManager::Operation operation,
QList<QPair<QByteArray,
QByteArray>> additional_headers = QList<QPair<QByteArray, QByteArray>>(),

View File

@ -329,7 +329,7 @@ void GmailNetworkFactory::onAuthFailed() {
});
}
bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList<Message>& lite_messages) {
bool GmailNetworkFactory::obtainAndDecodeFullMessages(QList<Message>& lite_messages) {
QHttpMultiPart* multi = new QHttpMultiPart();
multi->setContentType(QHttpMultiPart::ContentType::MixedType);
@ -354,7 +354,7 @@ bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList<Message>& lite
}
QList<QPair<QByteArray, QByteArray>> headers;
QList<QHttpPart*> output;
QList<HttpResponse> output;
int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
headers.append(QPair<QByteArray, QByteArray>(QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
@ -367,7 +367,7 @@ bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList<Message>& lite
QNetworkAccessManager::Operation::PostOperation,
headers).first == QNetworkReply::NetworkError::NoError) {
// We parse each part of HTTP response (it contains HTTP headers and payload with msg full data).
foreach (const HttpResponse& part, output) {}
}
else {
return false;

View File

@ -47,7 +47,7 @@ class GmailNetworkFactory : public QObject {
void onAuthFailed();
private:
bool obtainAndDecodeFullMessages(const QList<Message>& lite_messages);
bool obtainAndDecodeFullMessages(QList<Message>& lite_messages);
QList<Message> decodeLiteMessages(const QString& messages_json_data, const QString& stream_id, QString& next_page_token);
//RootItem* decodeFeedCategoriesData(const QString& categories);