1968 lines
74 KiB
C++
1968 lines
74 KiB
C++
/**********************************************************
|
|
static AeaQt::HttpClient client;
|
|
client.get("https://qthub.com")
|
|
.onSuccess([](QString result) { qDebug()<<"success!"; })
|
|
.onFailed([](QString error) { qDebug()<<"failed!"; })
|
|
.exec();
|
|
==========================================================
|
|
**********************************************************/
|
|
#ifndef QTHUB_COM_HTTPCLIENT_HPP
|
|
#define QTHUB_COM_HTTPCLIENT_HPP
|
|
|
|
#include <QRegularExpression>
|
|
#include <QNetworkReply>
|
|
#include <QHttpMultiPart>
|
|
#include <QAuthenticator>
|
|
|
|
#include <QJsonObject>
|
|
#include <QJsonDocument>
|
|
|
|
#include <QBuffer>
|
|
#include <QMetaEnum>
|
|
#include <QUrlQuery>
|
|
#include <QFileInfo>
|
|
|
|
#include <QTimer>
|
|
#include <QEventLoop>
|
|
#include <QDebug>
|
|
|
|
#include <functional>
|
|
|
|
namespace AeaQt {
|
|
|
|
class HttpClient;
|
|
class HttpRequest;
|
|
class HttpResponse;
|
|
|
|
class HttpClient : public QNetworkAccessManager
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
inline static HttpClient *instance();
|
|
inline HttpClient(QObject *parent = nullptr);
|
|
inline QString getVersion() {return "1.1.2";}
|
|
|
|
inline HttpRequest head(const QString &url);
|
|
inline HttpRequest get(const QString &url);
|
|
inline HttpRequest post(const QString &url);
|
|
inline HttpRequest put(const QString &url);
|
|
inline HttpRequest del(const QString &url);
|
|
|
|
inline HttpRequest send(const QString &url, Operation op = GetOperation);
|
|
|
|
private:
|
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 8, 0))
|
|
inline QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data);
|
|
inline QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart);
|
|
#endif
|
|
friend class HttpRequest;
|
|
};
|
|
|
|
class HttpRequest
|
|
{
|
|
public:
|
|
enum LogLevel { Off, Fatal, Error, Warn, Debug, Info, Trace, All};
|
|
|
|
inline explicit HttpRequest(QNetworkAccessManager::Operation op, HttpClient *httpClient);
|
|
inline virtual ~HttpRequest();
|
|
|
|
inline HttpRequest &url(const QString &url);
|
|
|
|
inline HttpRequest &header(const QString &key, const QVariant &value);
|
|
inline HttpRequest &header(QNetworkRequest::KnownHeaders header, const QVariant &value);
|
|
inline HttpRequest &headers(const QMap<QString, QVariant> &headers);
|
|
inline HttpRequest &headers(const QMap<QNetworkRequest::KnownHeaders, QVariant> &headers);
|
|
|
|
inline HttpRequest &queryParam(const QString &key, const QVariant &value);
|
|
inline HttpRequest &queryParams(const QMap<QString, QVariant> ¶ms);
|
|
|
|
/* Mainly used for identification */
|
|
inline HttpRequest &userAttribute(const QVariant &value);
|
|
inline HttpRequest &attribute(QNetworkRequest::Attribute attribute, const QVariant &value);
|
|
|
|
inline HttpRequest &body(const QByteArray &raw);
|
|
inline HttpRequest &bodyWithRaw(const QByteArray &raw);
|
|
|
|
inline HttpRequest &body(const QJsonObject &json);
|
|
inline HttpRequest &bodyWithJson(const QJsonObject &json);
|
|
|
|
inline HttpRequest &body(const QVariantMap &formUrlencodedMap);
|
|
inline HttpRequest &bodyWithFormUrlencoded(const QString &key, const QVariant &value);
|
|
inline HttpRequest &bodyWithFormUrlencoded(const QVariantMap &keyValueMap);
|
|
|
|
inline HttpRequest &bodyWithFormData(const QString &key, const QVariant &value);
|
|
inline HttpRequest &bodyWithFormData(const QVariantMap &keyValueMap);
|
|
|
|
inline HttpRequest &body(QHttpMultiPart *multiPart);
|
|
inline HttpRequest &bodyWithMultiPart(QHttpMultiPart *multiPart);
|
|
|
|
// multi-params
|
|
inline HttpRequest &body(const QString &key, const QString &file);
|
|
inline HttpRequest &bodyWithFile(const QString &key, const QString &file);
|
|
inline HttpRequest &bodyWithFile(const QMap<QString/*key*/, QString/*file*/> &fileMap); // => QMap<key, file>; like: { "key": "/home/example/car.jpeg" }
|
|
|
|
inline HttpRequest &ignoreSslErrors(const QList<QSslError> &errors);
|
|
inline HttpRequest &sslConfiguration(const QSslConfiguration &config);
|
|
|
|
inline HttpRequest &priority(QNetworkRequest::Priority priority);
|
|
inline HttpRequest &maximumRedirectsAllowed(int maxRedirectsAllowed);
|
|
inline HttpRequest &originatingObject(QObject *object);
|
|
inline HttpRequest &readBufferSize(qint64 size);
|
|
|
|
// Authentication
|
|
inline HttpRequest &autoAuthenticationRequired(const QAuthenticator &authenticator);
|
|
inline HttpRequest &autoAuthenticationRequired(const QString &user, const QString &password);
|
|
|
|
// 超过身份验证计数则触发失败并中断请求。
|
|
// count >= 0 => count
|
|
// count < 0 => infinite
|
|
inline HttpRequest &authenticationRequiredCount(int count = 1);
|
|
|
|
/**
|
|
* @brief msec <= 0, disable timeout
|
|
* msec > 0, enable timeout
|
|
*/
|
|
inline HttpRequest &timeout(const int &second = -1);
|
|
inline HttpRequest &timeoutMs(const int &msec = -1);
|
|
|
|
inline HttpRequest &download();
|
|
inline HttpRequest &download(const QString &file);
|
|
inline HttpRequest &enabledBreakpointDownload(bool enabled = true);
|
|
|
|
inline HttpRequest &retry(int count);
|
|
inline HttpRequest &repeat(int count);
|
|
|
|
/**
|
|
* @brief Block(or sync) current thread, entering an event loop.
|
|
*/
|
|
inline HttpRequest &block(bool isBlock = true);
|
|
inline HttpRequest &sync(bool isSync = true);
|
|
|
|
inline HttpRequest &logLevel(LogLevel level = Warn);
|
|
|
|
inline HttpResponse *exec();
|
|
|
|
// onFinished == onSuccess
|
|
inline HttpRequest &onSuccess(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onSuccess(std::function<void (QNetworkReply*)> lambda);
|
|
inline HttpRequest &onSuccess(std::function<void (QVariantMap)> lambda);
|
|
inline HttpRequest &onSuccess(std::function<void (QJsonObject)> lambda);
|
|
inline HttpRequest &onSuccess(std::function<void (QByteArray)> lambda);
|
|
inline HttpRequest &onSuccess(std::function<void ()> lambda);
|
|
|
|
// onFinished == onSuccess
|
|
inline HttpRequest &onFinished(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onFinished(std::function<void (QNetworkReply*)> lambda);
|
|
inline HttpRequest &onFinished(std::function<void (QVariantMap)> lambda);
|
|
inline HttpRequest &onFinished(std::function<void (QJsonObject)> lambda);
|
|
inline HttpRequest &onFinished(std::function<void (QByteArray)> lambda);
|
|
inline HttpRequest &onFinished(std::function<void ()> lambda);
|
|
|
|
// onError == onFailed
|
|
inline HttpRequest &onError(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onError(std::function<void (QNetworkReply*)> lambda);
|
|
inline HttpRequest &onError(std::function<void (QNetworkReply::NetworkError)> lambda);
|
|
inline HttpRequest &onError(std::function<void (QByteArray)> lambda);
|
|
inline HttpRequest &onError(std::function<void ()> lambda);
|
|
|
|
// onError == onFailed
|
|
inline HttpRequest &onFailed(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onFailed(std::function<void (QNetworkReply*)> lambda);
|
|
inline HttpRequest &onFailed(std::function<void (QNetworkReply::NetworkError)> lambda);
|
|
inline HttpRequest &onFailed(std::function<void (QByteArray)> lambda);
|
|
inline HttpRequest &onFailed(std::function<void ()> lambda);
|
|
|
|
inline HttpRequest &onReadyRead(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onReadyRead(std::function<void (QNetworkReply*)> lambda);
|
|
|
|
inline HttpRequest &onHead(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onHead(std::function<void (QList<QNetworkReply::RawHeaderPair>)> lambda);
|
|
inline HttpRequest &onHead(std::function<void (QMap<QString, QString>)> lambda);
|
|
|
|
inline HttpRequest &onTimeout(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onTimeout(std::function<void (QNetworkReply*)> lambda);
|
|
inline HttpRequest &onTimeout(std::function<void ()> lambda);
|
|
|
|
inline HttpRequest &onUploadProgress(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onUploadProgress(std::function<void (qint64, qint64)> lambda);
|
|
|
|
inline HttpRequest &onDownloadProgress(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onDownloadProgress(std::function<void (qint64, qint64)> lambda);
|
|
|
|
/// file download interface
|
|
inline HttpRequest &onDownloadFileNameChanged(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onDownloadFileNameChanged(std::function<void (QString/*fileName*/)> lambda);
|
|
|
|
inline HttpRequest &onDownloadFileProgress(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onDownloadFileProgress(std::function<void (qint64, qint64)> lambda);
|
|
|
|
inline HttpRequest &onDownloadFileSuccess(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onDownloadFileSuccess(std::function<void ()> lambda);
|
|
inline HttpRequest &onDownloadFileSuccess(std::function<void (QString/*fileName*/)> lambda);
|
|
|
|
inline HttpRequest &onDownloadFileFailed(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onDownloadFileFailed(std::function<void ()> lambda);
|
|
inline HttpRequest &onDownloadFileFailed(std::function<void (QString/*fileName*/)> lambda);
|
|
/// file download interface
|
|
|
|
inline HttpRequest &onRetried(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onRetried(std::function<void ()> lambda);
|
|
|
|
inline HttpRequest &onRepeated(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onRepeated(std::function<void ()> lambda);
|
|
|
|
inline HttpRequest &onAuthenticationRequired(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onAuthenticationRequired(std::function<void (QAuthenticator *)> lambda);
|
|
|
|
inline HttpRequest &onAuthenticationRequireFailed(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onAuthenticationRequireFailed(std::function<void ()> lambda);
|
|
inline HttpRequest &onAuthenticationRequireFailed(std::function<void (QNetworkReply *)> lambda);
|
|
|
|
// onResponse == onSuccess. note: DEPRECATED
|
|
inline HttpRequest &onResponse(const QObject *receiver, const char *method);
|
|
inline HttpRequest &onResponse(std::function<void (QNetworkReply*)> lambda);
|
|
inline HttpRequest &onResponse(std::function<void (QVariantMap)> lambda);
|
|
inline HttpRequest &onResponse(std::function<void (QByteArray)> lambda);
|
|
|
|
enum HandleType {
|
|
h_onFinished = 0,
|
|
h_onError,
|
|
h_onDownloadProgress,
|
|
h_onUploadProgress,
|
|
h_onTimeout,
|
|
h_onReadyRead,
|
|
h_onDownloadFileSuccess,
|
|
h_onDownloadFileFailed,
|
|
h_onEncrypted,
|
|
h_onMetaDataChanged,
|
|
h_onPreSharedKeyAuthenticationRequired,
|
|
h_onRedirectAllowed,
|
|
h_onRedirected,
|
|
h_onSslErrors,
|
|
h_onRetried,
|
|
h_onRepeated,
|
|
h_onAuthenticationRequired,
|
|
h_onAuthenticationRequireFailed,
|
|
h_onHead,
|
|
h_onDownloadFileProgess,
|
|
h_onDownloadFileNameChanged,
|
|
};
|
|
|
|
enum BodyType
|
|
{
|
|
None = 0, // This request does not have a body.
|
|
Raw,
|
|
Raw_Json, // application/json
|
|
X_Www_Form_Urlencoded, // x-www-form-urlencoded
|
|
FileMap, // multipart/form-data
|
|
MultiPart, // multipart/form-data
|
|
FormData // multipart/form-data
|
|
};
|
|
|
|
|
|
protected:
|
|
struct Downloader
|
|
{
|
|
bool isEnabled;
|
|
QString fileName;
|
|
bool enabledBreakpointDownload;
|
|
bool isSupportBreakpointDownload;
|
|
qint64 currentSize;
|
|
qint64 totalSize;
|
|
|
|
Downloader()
|
|
{
|
|
isEnabled = false;
|
|
fileName = "";
|
|
enabledBreakpointDownload = true;
|
|
isSupportBreakpointDownload = false;
|
|
totalSize = 0;
|
|
currentSize = 0;
|
|
}
|
|
};
|
|
|
|
QNetworkAccessManager::Operation m_op;
|
|
HttpClient *m_httpClient = nullptr;
|
|
QNetworkReply *m_reply = nullptr;
|
|
QNetworkRequest m_request;
|
|
|
|
QPair<BodyType, QVariant> m_body = qMakePair(BodyType::None, QByteArray());
|
|
int m_timeoutMs = -1; // ms
|
|
bool m_isBlock = false;
|
|
int m_retryCount = 0;
|
|
bool m_enabledRetry = false;
|
|
int m_repeatCount = 1;
|
|
QAuthenticator m_authenticator;
|
|
int m_authenticationRequiredCount = 1;
|
|
|
|
qint64 m_readBufferSize = -1;
|
|
QList<QSslError> m_ignoreSslErrors;
|
|
Downloader m_downloader;
|
|
QMap<HandleType, QList<QPair<QString, QVariant> > > m_handleMap;
|
|
QVariantMap m_formUrlencodedMap;
|
|
QVariantMap m_formDataMap;
|
|
LogLevel m_logLevel = Warn;
|
|
|
|
protected:
|
|
inline QString toString();
|
|
|
|
inline HttpRequest &enabledRetry(bool isEnabled);
|
|
inline HttpResponse *exec(const HttpRequest &httpRequest, HttpResponse *httpResponse = nullptr);
|
|
|
|
friend class HttpResponse;
|
|
friend class HttpDownloader;
|
|
|
|
private:
|
|
inline HttpRequest() = delete;
|
|
inline HttpRequest &onResponse(HandleType type, const QObject *receiver, const char *method);
|
|
inline HttpRequest &onResponse(HandleType type, QVariant lambda);
|
|
inline HttpRequest &onResponse(HandleType type, QString key, QVariant value);
|
|
};
|
|
|
|
class HttpResponse : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
inline explicit HttpResponse(const HttpRequest &httpRequest, QObject *parent = nullptr);
|
|
inline virtual ~HttpResponse();
|
|
|
|
inline void setHttpRequest(const HttpRequest &httpRequest);
|
|
inline QNetworkReply *reply() { return m_httpRequest.m_reply; }
|
|
inline QString toString() const;
|
|
signals:
|
|
void replyChanged(QNetworkReply *reply);
|
|
void finished(QNetworkReply *reply);
|
|
void finished();
|
|
void finished(QByteArray result);
|
|
void finished(QString result);
|
|
void finished(QVariantMap resultMap);
|
|
void finished(QJsonObject resultJsonObject);
|
|
|
|
void downloadProgress(qint64, qint64);
|
|
void uploadProgress(qint64, qint64);
|
|
|
|
void error(QByteArray error);
|
|
void error(QString error);
|
|
void error();
|
|
void error(QNetworkReply::NetworkError error);
|
|
void error(QNetworkReply *reply);
|
|
|
|
void timeout();
|
|
void timeout(QNetworkReply *reply);
|
|
|
|
void readyRead(QNetworkReply *reply);
|
|
|
|
void downloadFileNameChanged(QString fileName);
|
|
|
|
void downloadFileFinished();
|
|
void downloadFileFinished(QString file);
|
|
|
|
void downloadFileError();
|
|
void downloadFileError(QString errorString);
|
|
|
|
void encrypted();
|
|
void metaDataChanged();
|
|
|
|
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
|
|
void redirectAllowed();
|
|
void redirected(QUrl url);
|
|
void sslErrors(QList<QSslError> errors);
|
|
|
|
void retried();
|
|
void repeated();
|
|
|
|
void authenticationRequired(QAuthenticator *authentication);
|
|
void authenticationRequireFailed();
|
|
void authenticationRequireFailed(QNetworkReply *);
|
|
|
|
void head(QList<QNetworkReply::RawHeaderPair>);
|
|
void head(QMap<QString, QString>);
|
|
|
|
void downloadFileProgress(qint64 bytesReceived, qint64 bytesTotal);
|
|
|
|
private slots:
|
|
inline void onFinished();
|
|
inline void onError(QNetworkReply::NetworkError error);
|
|
inline void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
|
inline void onUploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
|
inline void onTimeout();
|
|
inline void onReadyRead();
|
|
inline void onReadOnceReplyHeader();
|
|
|
|
inline void onEncrypted();
|
|
inline void onMetaDataChanged();
|
|
inline void onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
|
|
inline void onRedirectAllowed();
|
|
inline void onRedirected(const QUrl &url);
|
|
inline void onSslErrors(const QList<QSslError> &errors);
|
|
|
|
inline void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
|
|
|
|
inline void onHandleHead();
|
|
|
|
private:
|
|
HttpRequest m_httpRequest;
|
|
QFile m_downloadFile;
|
|
int m_retriesRemaining = 0;
|
|
int m_authenticationCount = 0;
|
|
bool m_isHandleHead = false;
|
|
};
|
|
|
|
inline QString lineIndent(const QString &source, const QString &indentString);
|
|
inline QString networkOperation2String(QNetworkAccessManager::Operation o);
|
|
inline QString networkHeader2String(const QNetworkRequest &request);
|
|
inline QString networkBody2String(const QPair<HttpRequest::BodyType, QVariant> &body);
|
|
inline QString networkReplyHeader2String(const QNetworkReply *reply);
|
|
|
|
class HttpDownloader : public QObject
|
|
{
|
|
Q_OBJECT
|
|
HttpRequest m_httpRequest;
|
|
bool m_isSupportContinueDownload = false;
|
|
QString m_fileName;
|
|
qint64 m_contentLength = 0;
|
|
QList<QNetworkReply::RawHeaderPair> m_headerForPair;
|
|
QMap<QString, QString> m_headerForMap;
|
|
HttpResponse *m_response = nullptr;
|
|
|
|
public:
|
|
HttpDownloader(const HttpRequest &httpRequest, QObject *parent) : m_httpRequest(httpRequest), QObject(parent)
|
|
{
|
|
m_fileName = this->m_httpRequest.m_request.url().fileName();
|
|
if (m_fileName.isEmpty()) {
|
|
m_fileName = this->m_httpRequest.m_request.url().host();
|
|
}
|
|
}
|
|
|
|
virtual ~HttpDownloader() { }
|
|
|
|
HttpResponse *exec()
|
|
{
|
|
// todo: fixme
|
|
HttpClient &client = *HttpClient::instance();
|
|
client.get(m_httpRequest.m_request.url().toString())
|
|
.timeout(30)
|
|
.block(m_httpRequest.m_isBlock)
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
.attribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy) // Qt 6
|
|
#else
|
|
// .attribute(QNetworkRequest::FollowRedirectsAttribute, true)
|
|
.attribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy)
|
|
#endif
|
|
.onHead(this, SLOT(onHead(QList<QNetworkReply::RawHeaderPair>)))
|
|
.onHead(this, SLOT(onHead(QMap<QString,QString>)))
|
|
.onReadyRead(this, SLOT(onReadyRead(QNetworkReply*)))
|
|
.onFailed(this, SLOT(onResponse(QNetworkReply::NetworkError)))
|
|
.exec();
|
|
|
|
m_response = new HttpResponse(m_httpRequest, nullptr);
|
|
return m_response;
|
|
}
|
|
|
|
private slots:
|
|
void onResponse(QNetworkReply::NetworkError)
|
|
{
|
|
HttpResponse *response = m_httpRequest.exec(m_httpRequest, m_response);
|
|
this->setParent(response);
|
|
}
|
|
|
|
void onHead(QList<QNetworkReply::RawHeaderPair> headerForPair)
|
|
{
|
|
m_headerForPair = headerForPair;
|
|
}
|
|
|
|
void onHead(QMap<QString, QString> headerForMap)
|
|
{
|
|
m_headerForMap = headerForMap;
|
|
for (auto each: m_headerForMap.toStdMap()) {
|
|
QString key = each.first;
|
|
QString value = each.second;
|
|
|
|
if (key.contains("Content-Disposition", Qt::CaseInsensitive)) {
|
|
QString dispositionHeader = value;
|
|
// fixme rx
|
|
QRegularExpression rx("attachment;\\s*filename=([\\S]+)");
|
|
QRegularExpressionMatch match = rx.match(dispositionHeader);
|
|
if (match.hasMatch()) {
|
|
m_fileName = match.captured(1);
|
|
}
|
|
}
|
|
|
|
if (key.contains("Content-Length", Qt::CaseInsensitive)) {
|
|
m_contentLength = value.toLongLong();
|
|
}
|
|
|
|
if (key.contains("Content-Range", Qt::CaseInsensitive) ||
|
|
key.contains("Accept-Ranges", Qt::CaseInsensitive)) {
|
|
m_isSupportContinueDownload = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void onReadyRead(QNetworkReply *reply)
|
|
{
|
|
HttpResponse *response = new HttpResponse(m_httpRequest, m_httpRequest.m_reply);
|
|
if (m_httpRequest.m_downloader.fileName.isEmpty()) {
|
|
m_httpRequest.m_downloader.fileName = m_fileName;
|
|
emit response->downloadFileNameChanged(m_fileName);
|
|
}
|
|
|
|
m_httpRequest.m_downloader.totalSize = m_contentLength;
|
|
m_httpRequest.m_downloader.isSupportBreakpointDownload = m_isSupportContinueDownload;
|
|
|
|
if (m_httpRequest.m_downloader.enabledBreakpointDownload &&
|
|
m_httpRequest.m_downloader.isSupportBreakpointDownload)
|
|
{
|
|
QFile file(m_httpRequest.m_downloader.fileName);
|
|
if (file.exists() && file.open(QIODevice::ReadOnly)) {
|
|
m_httpRequest.m_downloader.currentSize = file.size();
|
|
|
|
if (file.size() == m_contentLength) {
|
|
emit response->head(m_headerForPair);
|
|
emit response->head(m_headerForMap);
|
|
|
|
emit response->downloadFileProgress(m_contentLength, m_contentLength);
|
|
|
|
emit response->downloadFileFinished();
|
|
emit response->downloadFileFinished(m_httpRequest.m_downloader.fileName);
|
|
|
|
emit response->finished();
|
|
emit response->finished(QString(""));
|
|
emit response->finished(QByteArray(""));
|
|
emit response->finished(QVariantMap{});
|
|
emit response->finished(nullptr);
|
|
|
|
response->deleteLater();
|
|
}
|
|
else if (file.size() > m_contentLength) {
|
|
file.close();
|
|
// Clear file content
|
|
file.open(QIODevice::Truncate);
|
|
file.close();
|
|
}
|
|
else {
|
|
m_httpRequest.m_request.setRawHeader("Range", QString("bytes=%1-").arg(file.size()).toUtf8());
|
|
}
|
|
}
|
|
}
|
|
|
|
response->deleteLater();
|
|
reply->abort();
|
|
}
|
|
};
|
|
|
|
class HttpResponseTimeout : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
HttpResponseTimeout(HttpResponse *parent, const int timeout = -1) : QObject(parent)
|
|
{
|
|
if (timeout > 0) {
|
|
QTimer::singleShot(timeout, parent, SLOT(onTimeout()));
|
|
}
|
|
else {
|
|
// do nothing
|
|
}
|
|
}
|
|
};
|
|
|
|
class HttpBlocker : public QEventLoop
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
HttpBlocker(QNetworkReply *reply) : QEventLoop(nullptr)
|
|
{
|
|
if (reply) {
|
|
connect(reply, SIGNAL(finished()), this, SLOT(quit()));
|
|
this->exec();
|
|
}
|
|
this->deleteLater();
|
|
}
|
|
};
|
|
|
|
#define _logger(l1, l2, str) \
|
|
do { \
|
|
if (l1 >= l2) { \
|
|
if (l2 >= HttpRequest::Debug) { \
|
|
qDebug().noquote() << str; \
|
|
} \
|
|
else if (l2 == HttpRequest::Warn) { \
|
|
qWarning().noquote() << str; \
|
|
} \
|
|
else if (l2 == HttpRequest::Error) { \
|
|
qCritical().noquote() << str; \
|
|
} \
|
|
else if (l2 == HttpRequest::Fatal) { \
|
|
qFatal("%s\n", str); \
|
|
} \
|
|
} \
|
|
} while(0);
|
|
|
|
#define printTrace(level, str) _logger(level, HttpRequest::Trace, str)
|
|
#define printInfo(level, str) _logger(level, HttpRequest::Info, str)
|
|
#define printDebug(level, str) _logger(level, HttpRequest::Debug, str)
|
|
#define printWarn(level, str) _logger(level, HttpRequest::Warn, str)
|
|
#define printError(level, str) _logger(level, HttpRequest::Error, str)
|
|
#define printFatal(level, str) _logger(level, HttpRequest::Fatal, str)
|
|
|
|
HttpRequest::~HttpRequest()
|
|
{
|
|
}
|
|
|
|
HttpRequest::HttpRequest(QNetworkAccessManager::Operation op, HttpClient *httpClient)
|
|
{
|
|
m_op = op;
|
|
m_httpClient = httpClient;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::url(const QString &url)
|
|
{
|
|
m_request.setUrl(QUrl(url));
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::header(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
|
{
|
|
m_request.setHeader(header, value);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::headers(const QMap<QNetworkRequest::KnownHeaders, QVariant> &headers)
|
|
{
|
|
QMapIterator<QNetworkRequest::KnownHeaders, QVariant> iter(headers);
|
|
while (iter.hasNext()) {
|
|
iter.next();
|
|
header(iter.key(), iter.value());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::header(const QString &key, const QVariant &value)
|
|
{
|
|
m_request.setRawHeader(QByteArray(key.toStdString().data()), QByteArray(value.toString().toStdString().data()));
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::headers(const QMap<QString, QVariant> &headers)
|
|
{
|
|
QMapIterator<QString, QVariant> iter(headers);
|
|
while (iter.hasNext()) {
|
|
iter.next();
|
|
header(iter.key(), iter.value());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::body(const QVariantMap &keyValueMap)
|
|
{
|
|
return bodyWithFormUrlencoded(keyValueMap);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithFormUrlencoded(const QString &key, const QVariant &value)
|
|
{
|
|
QVariantMap map;
|
|
map[key] = value;
|
|
|
|
return bodyWithFormUrlencoded(map);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithFormUrlencoded(const QVariantMap &keyValueMap)
|
|
{
|
|
// merge map
|
|
for (auto each : keyValueMap.toStdMap()) {
|
|
m_formUrlencodedMap[each.first] = each.second;
|
|
}
|
|
|
|
QString value;
|
|
QMapIterator<QString, QVariant> i(m_formUrlencodedMap);
|
|
while (i.hasNext()) {
|
|
i.next();
|
|
|
|
value += QString("%1=%2")
|
|
.arg(QUrl::toPercentEncoding(i.key()).data())
|
|
.arg(QUrl::toPercentEncoding(i.value().toString()).data());
|
|
if (i.hasNext()) {
|
|
value += "&";
|
|
}
|
|
}
|
|
|
|
m_body = qMakePair(X_Www_Form_Urlencoded, value);
|
|
m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
|
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithFormData(const QString &key, const QVariant &value)
|
|
{
|
|
QVariantMap map;
|
|
map[key] = value;
|
|
|
|
return bodyWithFormData(map);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithFormData(const QVariantMap &keyValueMap)
|
|
{
|
|
// merge map
|
|
for (auto each : keyValueMap.toStdMap()) {
|
|
m_formDataMap[each.first] = each.second;
|
|
}
|
|
|
|
m_body = qMakePair(FormData, m_formDataMap);
|
|
m_request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data");
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::body(const QJsonObject &json)
|
|
{
|
|
return bodyWithJson(json);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithJson(const QJsonObject &json)
|
|
{
|
|
const QByteArray &value = QJsonDocument(json).toJson();
|
|
m_body = qMakePair(Raw_Json, value);
|
|
m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::body(const QByteArray &raw)
|
|
{
|
|
return bodyWithRaw(raw);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithRaw(const QByteArray &raw)
|
|
{
|
|
m_body = qMakePair(Raw, raw);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::body(QHttpMultiPart *multiPart)
|
|
{
|
|
return bodyWithMultiPart(multiPart);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithMultiPart(QHttpMultiPart *multiPart)
|
|
{
|
|
m_body = qMakePair(MultiPart, QVariant::fromValue(multiPart));
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::download()
|
|
{
|
|
return download("");
|
|
}
|
|
|
|
HttpRequest &HttpRequest::download(const QString &file)
|
|
{
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
|
this->attribute(QNetworkRequest::RedirectPolicyAttribute, true);
|
|
#else
|
|
this->attribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
|
#endif
|
|
|
|
m_downloader.isEnabled = true;
|
|
m_downloader.fileName = file;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::enabledBreakpointDownload(bool enabled)
|
|
{
|
|
m_downloader.enabledBreakpointDownload = enabled;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::onDownloadFileProgress(const QObject *receiver, const char *method)
|
|
{
|
|
return onResponse(h_onDownloadFileProgess, receiver, method);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::onDownloadFileProgress(std::function<void (qint64, qint64)> lambda)
|
|
{
|
|
return onResponse(h_onDownloadFileProgess, QVariant::fromValue(lambda));
|
|
}
|
|
|
|
HttpRequest &HttpRequest::body(const QString &key, const QString &file)
|
|
{
|
|
return bodyWithFile(key, file);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithFile(const QString &key, const QString &filePath)
|
|
{
|
|
QMap<QString, QString> map;
|
|
map[key] = filePath;
|
|
|
|
return bodyWithFile(map);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::bodyWithFile(const QMap<QString, QString> &fileMap)
|
|
{
|
|
auto &body = m_body;
|
|
auto map = body.second.value<QMap<QString, QString>>();
|
|
for (auto each : fileMap.toStdMap()) {
|
|
map[each.first] = each.second;
|
|
}
|
|
|
|
body.first = BodyType::FileMap;
|
|
body.second = QVariant::fromValue(map);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::ignoreSslErrors(const QList<QSslError> &errors)
|
|
{
|
|
m_ignoreSslErrors = errors;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::sslConfiguration(const QSslConfiguration &config)
|
|
{
|
|
m_request.setSslConfiguration(config);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::priority(QNetworkRequest::Priority priority)
|
|
{
|
|
m_request.setPriority(priority);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::maximumRedirectsAllowed(int maxRedirectsAllowed)
|
|
{
|
|
m_request.setMaximumRedirectsAllowed(maxRedirectsAllowed);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::originatingObject(QObject *object)
|
|
{
|
|
m_request.setOriginatingObject(object);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::readBufferSize(qint64 size)
|
|
{
|
|
m_readBufferSize = size;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::autoAuthenticationRequired(const QAuthenticator &authenticator)
|
|
{
|
|
m_authenticator = authenticator;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::autoAuthenticationRequired(const QString &user, const QString &password)
|
|
{
|
|
QAuthenticator a;
|
|
a.setUser(user);
|
|
a.setPassword(password);
|
|
|
|
return autoAuthenticationRequired(a);
|
|
}
|
|
|
|
HttpRequest &HttpRequest::authenticationRequiredCount(int count)
|
|
{
|
|
m_authenticationRequiredCount = count;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::timeout(const int &second) { return timeoutMs(second * 1000); }
|
|
HttpRequest &HttpRequest::timeoutMs(const int &msec) { m_timeoutMs = msec; return *this; }
|
|
|
|
HttpRequest &HttpRequest::retry(int count) { m_retryCount = count; return *this; }
|
|
|
|
HttpRequest &HttpRequest::repeat(int count) { m_repeatCount = count; return *this; }
|
|
|
|
HttpRequest &HttpRequest::block(bool isBlock) { m_isBlock = isBlock; return *this; }
|
|
HttpRequest &HttpRequest::sync(bool isSync) { return block(isSync); }
|
|
|
|
HttpRequest &HttpRequest::logLevel(HttpRequest::LogLevel level) { m_logLevel = level; return *this; }
|
|
|
|
HttpRequest &HttpRequest::onFinished(const QObject *receiver, const char *method) { return onResponse(h_onFinished, receiver, method); }
|
|
HttpRequest &HttpRequest::onFinished(std::function<void (QNetworkReply *)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onFinished(std::function<void (QVariantMap)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onFinished(std::function<void (QJsonObject)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onFinished(std::function<void (QByteArray)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onFinished(std::function<void ()> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onSuccess(const QObject *receiver, const char *method) { return onFinished(receiver, method); }
|
|
HttpRequest &HttpRequest::onSuccess(std::function<void (QNetworkReply *)> lambda) { return onFinished(lambda); }
|
|
HttpRequest &HttpRequest::onSuccess(std::function<void (QVariantMap)> lambda) { return onFinished(lambda); }
|
|
HttpRequest &HttpRequest::onSuccess(std::function<void (QJsonObject)> lambda) { return onFinished(lambda); }
|
|
HttpRequest &HttpRequest::onSuccess(std::function<void (QByteArray)> lambda) { return onFinished(lambda); }
|
|
HttpRequest &HttpRequest::onSuccess(std::function<void ()> lambda) { return onFinished(lambda); }
|
|
|
|
HttpRequest &HttpRequest::onFailed(const QObject *receiver, const char *method) { return onError(receiver, method); }
|
|
HttpRequest &HttpRequest::onFailed(std::function<void (QNetworkReply *)> lambda) { return onError(lambda); }
|
|
HttpRequest &HttpRequest::onFailed(std::function<void (QNetworkReply::NetworkError)> lambda) { return onError(lambda); }
|
|
HttpRequest &HttpRequest::onFailed(std::function<void (QByteArray)> lambda) { return onError(lambda); }
|
|
HttpRequest &HttpRequest::onFailed(std::function<void ()> lambda) { return onError(lambda); }
|
|
|
|
HttpRequest &HttpRequest::onError(const QObject *receiver, const char *method) { return onResponse(h_onError, receiver, method); }
|
|
HttpRequest &HttpRequest::onError(std::function<void (QNetworkReply *)> lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onError(std::function<void (QNetworkReply::NetworkError)> lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onError(std::function<void (QByteArray)> lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onError(std::function<void ()> lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onReadyRead(const QObject *receiver, const char *method) { return onResponse(h_onReadyRead, receiver, method); }
|
|
HttpRequest &HttpRequest::onReadyRead(std::function<void (QNetworkReply *)> lambda) { return onResponse(h_onReadyRead, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onHead(const QObject *receiver, const char *method) { return onResponse(h_onHead, receiver, method); }
|
|
HttpRequest &HttpRequest::onHead(std::function<void (QList<QNetworkReply::RawHeaderPair>)> lambda) { return onResponse(h_onHead, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onHead(std::function<void (QMap<QString, QString>)> lambda) { return onResponse(h_onHead, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onDownloadProgress(const QObject *receiver, const char *method) { return onResponse(h_onDownloadProgress, receiver, method); }
|
|
HttpRequest &HttpRequest::onDownloadProgress(std::function<void (qint64, qint64)> lambda) { return onResponse(h_onDownloadProgress, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onDownloadFileNameChanged(const QObject *receiver, const char *method) { return onResponse(h_onDownloadFileNameChanged, receiver, method); }
|
|
HttpRequest &HttpRequest::onDownloadFileNameChanged(std::function<void (QString)> lambda) { return onResponse(h_onDownloadFileNameChanged, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onDownloadFileSuccess(const QObject *receiver, const char *method) { return onResponse(h_onDownloadFileSuccess, receiver, method); }
|
|
HttpRequest &HttpRequest::onDownloadFileSuccess(std::function<void ()> lambda) { return onResponse(h_onDownloadFileSuccess, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onDownloadFileSuccess(std::function<void (QString)> lambda) { return onResponse(h_onDownloadFileSuccess, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onDownloadFileFailed(const QObject *receiver, const char *method) { return onResponse(h_onDownloadFileFailed, receiver, method); }
|
|
HttpRequest &HttpRequest::onDownloadFileFailed(std::function<void ()> lambda) { return onResponse(h_onDownloadFileFailed, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onDownloadFileFailed(std::function<void (QString)> lambda) { return onResponse(h_onDownloadFileFailed, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onUploadProgress(const QObject *receiver, const char *method) { return onResponse(h_onUploadProgress, receiver, method); }
|
|
HttpRequest &HttpRequest::onUploadProgress(std::function<void (qint64, qint64)> lambda) { return onResponse(h_onUploadProgress, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onTimeout(const QObject *receiver, const char *method) { return onResponse(h_onTimeout, receiver, method); }
|
|
HttpRequest &HttpRequest::onTimeout(std::function<void (QNetworkReply *)> lambda) { return onResponse(h_onTimeout, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onTimeout(std::function<void ()> lambda) { return onResponse(h_onTimeout, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onRetried(const QObject *receiver, const char *method) { return onResponse(h_onRetried, receiver, method); }
|
|
HttpRequest &HttpRequest::onRetried(std::function<void ()> lambda) { return onResponse(h_onRetried, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onRepeated(const QObject *receiver, const char *method) { return onResponse(h_onRepeated, receiver, method); }
|
|
HttpRequest &HttpRequest::onRepeated(std::function<void ()> lambda) { return onResponse(h_onRepeated, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onAuthenticationRequired(const QObject *receiver, const char *method) { return onResponse(h_onAuthenticationRequired, receiver, method); }
|
|
HttpRequest &HttpRequest::onAuthenticationRequired(std::function<void (QAuthenticator *)> lambda) { return onResponse(h_onAuthenticationRequired, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onAuthenticationRequireFailed(const QObject *receiver, const char *method) { return onResponse(h_onAuthenticationRequireFailed, receiver, method); }
|
|
HttpRequest &HttpRequest::onAuthenticationRequireFailed(std::function<void ()> lambda) { return onResponse(h_onAuthenticationRequireFailed, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onAuthenticationRequireFailed(std::function<void (QNetworkReply *)> lambda) { return onResponse(h_onAuthenticationRequireFailed, QVariant::fromValue(lambda)); }
|
|
|
|
HttpRequest &HttpRequest::onResponse(const QObject *receiver, const char *method) { return onResponse(h_onFinished, receiver, method); }
|
|
HttpRequest &HttpRequest::onResponse(std::function<void (QNetworkReply*)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onResponse(std::function<void (QVariantMap)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onResponse(std::function<void (QByteArray)> lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); }
|
|
HttpRequest &HttpRequest::onResponse(HandleType type, QVariant lambda) { return onResponse(type, lambda.typeName(), lambda); }
|
|
HttpRequest &HttpRequest::onResponse(HandleType type, const QObject *receiver, const char *method) { return onResponse(type, method, QVariant::fromValue((QObject *)receiver)); }
|
|
HttpRequest &HttpRequest::onResponse(HandleType type, QString key, QVariant value)
|
|
{
|
|
if (!m_handleMap.contains(type)) {
|
|
QList<QPair<QString, QVariant> > handleList;
|
|
m_handleMap.insert(type, handleList);
|
|
}
|
|
|
|
auto handleList = m_handleMap[type];
|
|
handleList.append({key, value});
|
|
|
|
m_handleMap.insert(type, handleList);
|
|
return *this;
|
|
}
|
|
|
|
QString HttpRequest::toString()
|
|
{
|
|
QString str = \
|
|
"General: \n" \
|
|
" Request URL: %{url} \n" \
|
|
" Request Method: %{method} \n" \
|
|
"Request Headers: \n" \
|
|
"%{requestHeaders} \n" \
|
|
"Request Body: \n" \
|
|
"%{requestBody}";
|
|
|
|
str.replace("%{url}", m_request.url().toString());
|
|
str.replace("%{method}", networkOperation2String(m_op));
|
|
str.replace("%{requestHeaders}", lineIndent(networkHeader2String(m_request), " "));
|
|
str.replace("%{requestBody}", lineIndent(networkBody2String(m_body), " "));
|
|
|
|
return str;
|
|
}
|
|
|
|
HttpResponse *HttpRequest::exec(const HttpRequest &_httpRequest, HttpResponse *httpResponse)
|
|
{
|
|
HttpRequest httpRequest = _httpRequest;
|
|
|
|
QByteArray op = networkOperation2String(httpRequest.m_op).toUtf8();
|
|
if (op.isEmpty()) {
|
|
QString str = QString("Url: [%1]; Method: [%2] not support!").arg(httpRequest.m_request.url().toString()).arg(QString(op));
|
|
printError(httpRequest.m_logLevel, str);
|
|
return nullptr;
|
|
}
|
|
|
|
using BodyType = HttpRequest::BodyType;
|
|
BodyType bodyType = httpRequest.m_body.first;
|
|
QVariant body = httpRequest.m_body.second;
|
|
QNetworkRequest request = httpRequest.m_request;
|
|
HttpClient *httpClient = httpRequest.m_httpClient;
|
|
|
|
if (bodyType == BodyType::MultiPart) {
|
|
QHttpMultiPart *multiPart = body.value<QHttpMultiPart*>();
|
|
QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data());
|
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
|
|
|
|
httpRequest.m_reply = httpRequest.m_httpClient->sendCustomRequest(request, op, multiPart);
|
|
multiPart->setParent(httpRequest.m_reply);
|
|
|
|
}
|
|
else if (bodyType == BodyType::FileMap) {
|
|
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
|
QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data());
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
|
|
|
|
const auto &fileMap = body.value<QMap<QString, QString>>();
|
|
for (const auto &each : fileMap.toStdMap()) {
|
|
const QString &key = each.first;
|
|
const QString &filePath = each.second;
|
|
|
|
QFile *file = new QFile(filePath);
|
|
file->open(QIODevice::ReadOnly);
|
|
file->setParent(multiPart);
|
|
|
|
// todo
|
|
// part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
|
|
|
|
// note: "form-data; name=\"%1\";filename=\"%2\"" != "form-data; name=\"%1\";filename=\"%2\";"
|
|
QString dispositionHeader = QString("form-data; name=\"%1\";filename=\"%2\"")
|
|
.arg(key)
|
|
.arg(QFileInfo(filePath).fileName());
|
|
QHttpPart part;
|
|
part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader);
|
|
part.setBodyDevice(file);
|
|
|
|
multiPart->append(part);
|
|
}
|
|
|
|
httpRequest.m_reply = httpClient->sendCustomRequest(request, op, multiPart);
|
|
if (httpRequest.m_reply)
|
|
multiPart->setParent(httpRequest.m_reply);
|
|
else
|
|
delete multiPart;
|
|
|
|
}
|
|
else if (bodyType == BodyType::FormData) {
|
|
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
|
QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data());
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
|
|
|
|
const auto &formDataMap = body.value<QMap<QString, QVariant>>();
|
|
for (const auto &each : formDataMap.toStdMap()) {
|
|
const QString &key = each.first;
|
|
const QString &value = each.second.toString();
|
|
|
|
QString dispositionHeader = QString("form-data; name=\"%1\"").arg(key);
|
|
|
|
QHttpPart part;
|
|
part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader);
|
|
part.setBody(value.toUtf8());
|
|
|
|
multiPart->append(part);
|
|
}
|
|
|
|
httpRequest.m_reply = httpClient->sendCustomRequest(request, op, multiPart);
|
|
|
|
if (httpRequest.m_reply)
|
|
multiPart->setParent(httpRequest.m_reply);
|
|
else
|
|
delete multiPart;
|
|
}
|
|
else {
|
|
httpRequest.m_reply = httpClient->sendCustomRequest(request, op, body.toByteArray());
|
|
}
|
|
|
|
if (httpRequest.m_reply == nullptr) {
|
|
// fixme: todo onError
|
|
printError(httpRequest.m_logLevel, "Http reply invalid");
|
|
Q_ASSERT(httpRequest.m_reply);
|
|
return nullptr;
|
|
}
|
|
|
|
static int count = 0;
|
|
httpRequest.m_reply->setProperty("count", count++);
|
|
|
|
// fixme
|
|
if (!httpRequest.m_ignoreSslErrors.isEmpty()) {
|
|
httpRequest.m_reply->ignoreSslErrors(httpRequest.m_ignoreSslErrors);
|
|
}
|
|
|
|
if (httpRequest.m_readBufferSize >= 0) {
|
|
httpRequest.m_reply->setReadBufferSize(httpRequest.m_readBufferSize);
|
|
}
|
|
|
|
printDebug(httpRequest.m_logLevel, toString());
|
|
|
|
if (httpResponse) {
|
|
httpResponse->setParent(httpRequest.m_reply);
|
|
httpResponse->setHttpRequest(httpRequest);
|
|
return httpResponse;
|
|
}
|
|
else {
|
|
return new HttpResponse(httpRequest, httpRequest.m_reply);
|
|
}
|
|
}
|
|
|
|
inline QDebug operator<<(QDebug debug, const QNetworkAccessManager::Operation &op)
|
|
{
|
|
QDebugStateSaver saver(debug);
|
|
debug.nospace();
|
|
|
|
switch (op) {
|
|
case QNetworkAccessManager::HeadOperation: return debug << "HeadOperation";
|
|
case QNetworkAccessManager::GetOperation: return debug << "GetOperation";
|
|
case QNetworkAccessManager::PostOperation: return debug << "PostOperation";
|
|
case QNetworkAccessManager::PutOperation: return debug << "PutOperation";
|
|
case QNetworkAccessManager::DeleteOperation: return debug << "DeleteOperation";
|
|
case QNetworkAccessManager::CustomOperation: return debug << "CustomOperation";
|
|
default: return debug << "UnknownOperation";
|
|
}
|
|
}
|
|
|
|
inline QDebug operator<<(QDebug debug, const HttpRequest::HandleType &handleType)
|
|
{
|
|
QDebugStateSaver saver(debug);
|
|
debug.nospace();
|
|
|
|
switch (handleType) {
|
|
case HttpRequest::h_onFinished: return debug << "onFinished";
|
|
case HttpRequest::h_onError: return debug << "onError";
|
|
case HttpRequest::h_onDownloadProgress: return debug << "onDownloadProgress";
|
|
case HttpRequest::h_onUploadProgress: return debug << "onUploadProgress";
|
|
// todo: onUploadProgressSuccess and onUploadProgressFaied
|
|
case HttpRequest::h_onDownloadFileSuccess: return debug << "onDownloadFileSuccess";
|
|
case HttpRequest::h_onDownloadFileFailed: return debug << "onDownloadFileFailed";
|
|
case HttpRequest::h_onTimeout: return debug << "onTimeout";
|
|
case HttpRequest::h_onReadyRead: return debug << "onReadyRead";
|
|
case HttpRequest::h_onEncrypted: return debug << "onEncrypted";
|
|
case HttpRequest::h_onMetaDataChanged: return debug << "onMetaChanged";
|
|
case HttpRequest::h_onPreSharedKeyAuthenticationRequired: return debug << "onPreSharedKeyAuthenticationRequired";
|
|
case HttpRequest::h_onRedirectAllowed: return debug << "onRedirectAllowed";
|
|
case HttpRequest::h_onRedirected: return debug << "onRedirected";
|
|
case HttpRequest::h_onSslErrors: return debug << "onSslErrors";
|
|
case HttpRequest::h_onRetried: return debug << "onRetried";
|
|
case HttpRequest::h_onRepeated: return debug << "onRepeated";
|
|
case HttpRequest::h_onAuthenticationRequired: return debug << "onAuthenticationRequired";
|
|
case HttpRequest::h_onAuthenticationRequireFailed: return debug << "onAuthenticationRequireFailed";
|
|
case HttpRequest::h_onHead: return debug << "onHead";
|
|
case HttpRequest::h_onDownloadFileProgess: return debug << "onDownloadFileProgress";
|
|
case HttpRequest::h_onDownloadFileNameChanged: return debug << "onDownloadFileNameChanged";
|
|
default: return debug << "Unknow";
|
|
}
|
|
}
|
|
|
|
static int extractCode(const char *member)
|
|
{
|
|
/* extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE */
|
|
return (((int)(*member) - '0') & 0x3);
|
|
}
|
|
|
|
static bool isMethod(const char *member)
|
|
{
|
|
int ret = extractCode(member);
|
|
return ret >= QMETHOD_CODE && ret <= QSIGNAL_CODE;
|
|
}
|
|
|
|
template<typename M, typename L, typename T>
|
|
bool httpResponseConnect(L sender, T senderSignal, const QString &lambdaString, const QVariant &lambda)
|
|
{
|
|
if (lambdaString == QVariant::fromValue(M()).typeName()) {
|
|
return QObject::connect(sender, senderSignal, lambda.value<M>());
|
|
}
|
|
else if (isMethod(qPrintable(lambdaString))) {
|
|
QString signal = QMetaMethod::fromSignal(senderSignal).methodSignature();
|
|
signal.insert(0, "2");
|
|
signal.replace("qlonglong", "qint64");
|
|
|
|
const QObject *receiver = lambda.value<QObject*>();
|
|
QString method = QMetaObject::normalizedSignature(qPrintable(lambdaString)); // remove 'const', like: const QString => QString
|
|
|
|
if (QMetaObject::checkConnectArgs(qPrintable(signal), qPrintable(method))) {
|
|
return QObject::connect(sender, qPrintable(signal), receiver, qPrintable(method));
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#define HTTP_RESPONSE_CONNECT_X(sender, senderSignal, lambdaString, lambda, ...) \
|
|
httpResponseConnect< std::function<void (__VA_ARGS__)> > ( \
|
|
sender, \
|
|
static_cast<void (HttpResponse::*)(__VA_ARGS__)>(&HttpResponse::senderSignal), \
|
|
lambdaString, \
|
|
lambda);
|
|
|
|
HttpResponse *HttpRequest::exec()
|
|
{
|
|
if (this->m_downloader.isEnabled) {
|
|
HttpDownloader *downloader = new HttpDownloader(*this, nullptr);
|
|
HttpResponse *response = downloader->exec();
|
|
downloader->setParent(response);
|
|
return response;
|
|
}
|
|
else {
|
|
return exec(*this);
|
|
}
|
|
}
|
|
|
|
HttpRequest &HttpRequest::enabledRetry(bool isEnabled)
|
|
{
|
|
m_enabledRetry = isEnabled;
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::queryParam(const QString &key, const QVariant &value)
|
|
{
|
|
QUrl url(m_request.url());
|
|
QUrlQuery urlQuery(url);
|
|
|
|
urlQuery.addQueryItem(key, value.toString());
|
|
url.setQuery(urlQuery);
|
|
|
|
m_request.setUrl(url);
|
|
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::queryParams(const QMap<QString, QVariant> ¶ms)
|
|
{
|
|
QMapIterator<QString, QVariant> iter(params);
|
|
while (iter.hasNext()) {
|
|
iter.next();
|
|
queryParam(iter.key(), iter.value());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::userAttribute(const QVariant &value)
|
|
{
|
|
m_request.setAttribute(QNetworkRequest::User, value);
|
|
return *this;
|
|
}
|
|
|
|
HttpRequest &HttpRequest::attribute(QNetworkRequest::Attribute attribute, const QVariant &value)
|
|
{
|
|
m_request.setAttribute(attribute, value);
|
|
return *this;
|
|
}
|
|
|
|
HttpClient *HttpClient::instance()
|
|
{
|
|
static HttpClient client;
|
|
return &client;
|
|
}
|
|
|
|
HttpClient::HttpClient(QObject *parent) : QNetworkAccessManager(parent)
|
|
{
|
|
}
|
|
|
|
HttpRequest HttpClient::head(const QString &url)
|
|
{
|
|
return HttpRequest(QNetworkAccessManager::HeadOperation, this).url(url);
|
|
}
|
|
|
|
HttpRequest HttpClient::get(const QString &url)
|
|
{
|
|
return HttpRequest(QNetworkAccessManager::GetOperation, this).url(url);
|
|
}
|
|
|
|
HttpRequest HttpClient::post(const QString &url)
|
|
{
|
|
return HttpRequest(QNetworkAccessManager::PostOperation, this).url(url);
|
|
}
|
|
|
|
HttpRequest HttpClient::put(const QString &url)
|
|
{
|
|
return HttpRequest(QNetworkAccessManager::PutOperation, this).url(url);
|
|
}
|
|
HttpRequest HttpClient::del(const QString &url)
|
|
{
|
|
return HttpRequest(QNetworkAccessManager::DeleteOperation, this).url(url);
|
|
}
|
|
|
|
HttpRequest HttpClient::send(const QString &url, QNetworkAccessManager::Operation op)
|
|
{
|
|
return HttpRequest(op, this).url(url);
|
|
}
|
|
|
|
#if (QT_VERSION < QT_VERSION_CHECK(5, 8, 0))
|
|
QNetworkReply *HttpClient::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data)
|
|
{
|
|
QBuffer *buffer = new QBuffer;
|
|
buffer->setData(data);
|
|
buffer->open(QIODevice::ReadOnly);
|
|
QNetworkReply *reply = QNetworkAccessManager::sendCustomRequest(request, verb, buffer);
|
|
buffer->setParent(reply);
|
|
|
|
return reply;
|
|
}
|
|
|
|
QNetworkReply *HttpClient::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart)
|
|
{
|
|
if (verb == "PUT") {
|
|
return QNetworkAccessManager::put(request, multiPart);
|
|
}
|
|
else if (verb == "POST") {
|
|
return QNetworkAccessManager::post(request, multiPart);
|
|
}
|
|
else {
|
|
qWarning() << "not support " << verb << "multi part.";
|
|
return nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
HttpResponse::HttpResponse(const HttpRequest &httpRequest, QObject *parent)
|
|
: QObject(parent),
|
|
m_httpRequest(httpRequest),
|
|
m_retriesRemaining(httpRequest.m_retryCount)
|
|
{
|
|
this->setHttpRequest(httpRequest);
|
|
}
|
|
|
|
HttpResponse::~HttpResponse()
|
|
{
|
|
}
|
|
|
|
void HttpResponse::setHttpRequest(const HttpRequest &httpRequest)
|
|
{
|
|
if (httpRequest.m_timeoutMs > 0) {
|
|
new HttpResponseTimeout(this, httpRequest.m_timeoutMs);
|
|
}
|
|
|
|
QNetworkReply *reply = httpRequest.m_reply;
|
|
if (reply) {
|
|
connect(reply, SIGNAL(finished()), this, SLOT(onFinished()));
|
|
connect(reply, SIGNAL(finished()), this, SLOT(onHandleHead()));
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
|
|
#else
|
|
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
|
|
#endif
|
|
|
|
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
|
|
connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(onUploadProgress(qint64, qint64)));
|
|
|
|
connect(reply, SIGNAL(readyRead()), this, SLOT(onReadOnceReplyHeader()));
|
|
connect(reply, SIGNAL(readyRead()), this, SLOT(onHandleHead()));
|
|
connect(reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
|
|
|
|
connect(reply, SIGNAL(encrypted()), this, SLOT(onEncrypted()));
|
|
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(onMetaDataChanged()));
|
|
|
|
connect(reply, SIGNAL(redirected(QUrl)), this, SLOT(onRedirected(QUrl)));
|
|
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(onSslErrors(QList<QSslError>)));
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
|
connect(reply, SIGNAL(redirectAllowed()), this, SLOT(onRedirectAllowed()));
|
|
#endif
|
|
|
|
connect(reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), this, SLOT(onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
|
|
|
|
connect(reply->manager(), SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
|
|
|
|
// fixme: Too cumbersome
|
|
for (auto each : httpRequest.m_handleMap.toStdMap()) {
|
|
const HttpRequest::HandleType &key = each.first;
|
|
const QList<QPair<QString, QVariant>> &value = each.second;
|
|
|
|
for (auto iter : value) {
|
|
const QVariant &lambda = iter.second;
|
|
const QString &lambdaString = iter.first;
|
|
int ret = 0;
|
|
|
|
if (key == HttpRequest::h_onFinished) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, void);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QByteArray);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QString);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QVariantMap);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QJsonObject);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QNetworkReply*);
|
|
}
|
|
else if (key == HttpRequest::h_onDownloadProgress) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadProgress, lambdaString, lambda, qint64, qint64);
|
|
}
|
|
else if (key == HttpRequest::h_onUploadProgress) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, uploadProgress, lambdaString, lambda, qint64, qint64);
|
|
}
|
|
else if (key == HttpRequest::h_onError) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, void);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QByteArray);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QString);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QNetworkReply*);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QNetworkReply::NetworkError);
|
|
}
|
|
else if (key == HttpRequest::h_onTimeout) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, timeout, lambdaString, lambda, QNetworkReply*);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, timeout, lambdaString, lambda, void);
|
|
}
|
|
else if (key == HttpRequest::h_onReadyRead) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, readyRead, lambdaString, lambda, QNetworkReply*);
|
|
}
|
|
else if (key == HttpRequest::h_onDownloadFileSuccess) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileFinished, lambdaString, lambda, void);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileFinished, lambdaString, lambda, QString);
|
|
}
|
|
else if (key == HttpRequest::h_onDownloadFileFailed) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileError, lambdaString, lambda, void);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileError, lambdaString, lambda, QString);
|
|
}
|
|
else if (key == HttpRequest::h_onEncrypted) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, encrypted, lambdaString, lambda, void);
|
|
}
|
|
else if (key == HttpRequest::h_onMetaDataChanged) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, metaDataChanged, lambdaString, lambda, void);
|
|
}
|
|
else if (key == HttpRequest::h_onPreSharedKeyAuthenticationRequired) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, preSharedKeyAuthenticationRequired, lambdaString, lambda, QSslPreSharedKeyAuthenticator*);
|
|
}
|
|
else if (key == HttpRequest::h_onRedirectAllowed) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, redirectAllowed, lambdaString, lambda, void);
|
|
}
|
|
else if (key == HttpRequest::h_onRedirected) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, redirected, lambdaString, lambda, QUrl);
|
|
}
|
|
else if (key == HttpRequest::h_onSslErrors) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, sslErrors, lambdaString, lambda, QList<QSslError>);
|
|
}
|
|
else if (key == HttpRequest::h_onRetried) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, retried, lambdaString, lambda, void);
|
|
}
|
|
else if (key == HttpRequest::h_onRepeated) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, repeated, lambdaString, lambda, void);
|
|
}
|
|
else if (key == HttpRequest::h_onAuthenticationRequired) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, authenticationRequired, lambdaString, lambda, QAuthenticator*);
|
|
}
|
|
else if (key == HttpRequest::h_onAuthenticationRequireFailed) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, authenticationRequireFailed, lambdaString, lambda, void);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, authenticationRequireFailed, lambdaString, lambda, QNetworkReply*);
|
|
}
|
|
else if (key == HttpRequest::h_onHead) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, head, lambdaString, lambda, QList<QNetworkReply::RawHeaderPair>);
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, head, lambdaString, lambda, QMap<QString, QString>);
|
|
}
|
|
else if (key == HttpRequest::h_onDownloadFileProgess) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileProgress, lambdaString, lambda, qint64, qint64);
|
|
}
|
|
else if (key == HttpRequest::h_onDownloadFileNameChanged) {
|
|
ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileNameChanged, lambdaString, lambda, QString);
|
|
}
|
|
else {
|
|
printWarn(httpRequest.m_logLevel, QString("%1 unsupported").arg(key));
|
|
}
|
|
|
|
if (ret == 0) {
|
|
QString method = lambdaString;
|
|
if (isMethod(qPrintable(method)))
|
|
method.remove(0, 1);
|
|
|
|
printWarn(httpRequest.m_logLevel, QString("%1 method[%2] is invalid").arg(key).arg(method));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const HttpRequest &oldRequest = m_httpRequest;
|
|
m_httpRequest = httpRequest;
|
|
|
|
if (oldRequest.m_reply != httpRequest.m_reply) {
|
|
emit replyChanged(httpRequest.m_reply);
|
|
}
|
|
|
|
if (reply && httpRequest.m_isBlock) {
|
|
new HttpBlocker(reply);
|
|
}
|
|
}
|
|
|
|
QString HttpResponse::toString() const
|
|
{
|
|
QString str = \
|
|
"General: \n" \
|
|
" Request URL: %{url} \n" \
|
|
" Request Method: %{method} \n" \
|
|
" Request Status: %{status}(%{statusString}) \n" \
|
|
"Request Headers: \n" \
|
|
"%{requestHeaders} \n" \
|
|
"Response Headers: \n" \
|
|
"%{responseHeaders} \n" \
|
|
"Request Body: \n" \
|
|
"%{requestBody}";
|
|
|
|
QNetworkReply *reply = this->m_httpRequest.m_reply;
|
|
str.replace("%{url}", this->m_httpRequest.m_request.url().toString());
|
|
str.replace("%{method}", networkOperation2String(m_httpRequest.m_op));
|
|
str.replace("%{status}", QString::number(reply->error()));
|
|
str.replace("%{statusString}", reply->errorString());
|
|
str.replace("%{requestHeaders}", lineIndent(networkHeader2String(reply->request()), " "));
|
|
str.replace("%{responseHeaders}", lineIndent(networkReplyHeader2String(reply), " "));
|
|
str.replace("%{requestBody}", lineIndent(networkBody2String(m_httpRequest.m_body), " "));
|
|
return str;
|
|
}
|
|
|
|
void HttpResponse::onFinished()
|
|
{
|
|
QNetworkReply *reply = m_httpRequest.m_reply;
|
|
if (reply->error() != QNetworkReply::NoError) {
|
|
return;
|
|
}
|
|
|
|
for (QObject *o : reply->children()) {
|
|
HttpResponse *response = qobject_cast<HttpResponse*>(o);
|
|
if (response) {
|
|
printDebug(m_httpRequest.m_logLevel, response->toString());
|
|
}
|
|
}
|
|
|
|
if (m_httpRequest.m_enabledRetry) {
|
|
emit retried();
|
|
}
|
|
|
|
if (m_downloadFile.isOpen()) {
|
|
emit downloadFileFinished();
|
|
emit downloadFileFinished(m_downloadFile.fileName());
|
|
|
|
m_downloadFile.close();
|
|
}
|
|
|
|
bool isAutoDelete = true;
|
|
if (this->receivers(SIGNAL(finished(QNetworkReply*))) > 0) {
|
|
emit finished(reply);
|
|
isAutoDelete = false;
|
|
}
|
|
|
|
if (this->receivers(SIGNAL(finished())) > 0 ||
|
|
this->receivers(SIGNAL(finished(QByteArray))) > 0 ||
|
|
this->receivers(SIGNAL(finished(QString))) > 0 ||
|
|
this->receivers(SIGNAL(finished(QVariantMap))) > 0 ||
|
|
this->receivers(SIGNAL(finished(QJsonObject))) > 0
|
|
)
|
|
{
|
|
QByteArray result = reply->readAll();
|
|
emit finished();
|
|
|
|
emit finished(result);
|
|
|
|
emit finished(QString(result));
|
|
|
|
QJsonObject json = QJsonDocument::fromJson(result).object();
|
|
emit finished(json);
|
|
|
|
QVariantMap resultMap = json.toVariantMap();
|
|
emit finished(resultMap);
|
|
}
|
|
|
|
if (--m_httpRequest.m_repeatCount > 0) {
|
|
HttpRequest httpRequest = m_httpRequest;
|
|
httpRequest.repeat(m_httpRequest.m_repeatCount)
|
|
.exec();
|
|
}
|
|
else {
|
|
emit repeated();
|
|
}
|
|
|
|
if (isAutoDelete) {
|
|
reply->deleteLater();
|
|
}
|
|
}
|
|
|
|
void HttpResponse::onError(QNetworkReply::NetworkError error)
|
|
{
|
|
QNetworkReply *reply = m_httpRequest.m_reply;
|
|
|
|
printInfo(m_httpRequest.m_logLevel, QString("%1 error: %2").arg(reply->url().toString()).arg(error));
|
|
|
|
if ( m_retriesRemaining-- > 0) {
|
|
HttpRequest httpRequest = m_httpRequest;
|
|
httpRequest.retry(m_retriesRemaining)
|
|
.enabledRetry(true)
|
|
.exec();
|
|
reply->deleteLater();
|
|
return;
|
|
}
|
|
|
|
if (m_httpRequest.m_enabledRetry) {
|
|
emit retried();
|
|
}
|
|
|
|
const QMetaObject &metaObject = QNetworkReply::staticMetaObject;
|
|
QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("NetworkError"));
|
|
QString errorString = reply->errorString().isEmpty() ? metaEnum.valueToKey(error) : reply->errorString();
|
|
|
|
if (m_httpRequest.m_downloader.isEnabled) {
|
|
QString error = QString("Url: %1 file: %2 error: %3")
|
|
.arg(m_httpRequest.m_request.url().toString()) // fixme
|
|
.arg(m_downloadFile.fileName())
|
|
.arg(errorString);
|
|
|
|
emit downloadFileError();
|
|
emit downloadFileError(error);
|
|
|
|
m_downloadFile.close();
|
|
}
|
|
|
|
bool isAutoDelete = true;
|
|
if (this->receivers(SIGNAL(error(QNetworkReply*))) > 0) {
|
|
emit this->error(reply);
|
|
isAutoDelete = false;
|
|
}
|
|
|
|
emit this->error();
|
|
emit this->error(error);
|
|
emit this->error(errorString);
|
|
emit this->error(errorString.toLocal8Bit());
|
|
|
|
if (--m_httpRequest.m_repeatCount > 0) {
|
|
HttpRequest httpRequest = m_httpRequest;
|
|
httpRequest.repeat(m_httpRequest.m_repeatCount)
|
|
.exec();
|
|
}
|
|
else {
|
|
emit repeated();
|
|
}
|
|
|
|
if (isAutoDelete) {
|
|
reply->deleteLater();
|
|
}
|
|
}
|
|
|
|
void HttpResponse::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
|
{
|
|
emit this->downloadProgress(bytesReceived, bytesTotal);
|
|
}
|
|
|
|
void HttpResponse::onUploadProgress(qint64 bytesSent, qint64 bytesTotal)
|
|
{
|
|
emit this->uploadProgress(bytesSent, bytesTotal);
|
|
}
|
|
|
|
void HttpResponse::onTimeout()
|
|
{
|
|
QNetworkReply *reply = m_httpRequest.m_reply;
|
|
if (reply && reply->isRunning()) {
|
|
reply->abort();
|
|
|
|
bool isAutoDelete = true;
|
|
if (this->receivers(SIGNAL(timeout(QNetworkReply*))) > 0) {
|
|
emit this->timeout(reply);
|
|
isAutoDelete = false;
|
|
}
|
|
|
|
if (this->receivers(SIGNAL(timeout())) > 0) {
|
|
emit this->timeout();
|
|
}
|
|
|
|
if (isAutoDelete) {
|
|
reply->deleteLater();
|
|
}
|
|
}
|
|
}
|
|
|
|
void HttpResponse::onReadyRead()
|
|
{
|
|
QNetworkReply *reply = m_httpRequest.m_reply;
|
|
if (m_httpRequest.m_downloader.isEnabled) {
|
|
if (m_downloadFile.isOpen()){
|
|
int size = m_downloadFile.write(reply->readAll());
|
|
if (size == -1) {
|
|
QString error = QString("Url: %1 %2 Write failed!")
|
|
.arg(m_httpRequest.m_request.url().toString())
|
|
.arg(m_downloadFile.fileName());
|
|
emit downloadFileError();
|
|
emit downloadFileError(error);
|
|
}
|
|
else {
|
|
m_httpRequest.m_downloader.currentSize += size;
|
|
emit downloadFileProgress(m_httpRequest.m_downloader.currentSize, m_httpRequest.m_downloader.totalSize);
|
|
}
|
|
}
|
|
else {
|
|
// do nothing
|
|
}
|
|
}
|
|
else {
|
|
// do nothing
|
|
}
|
|
|
|
emit readyRead(reply);
|
|
}
|
|
|
|
void HttpResponse::onReadOnceReplyHeader()
|
|
{
|
|
if (! m_httpRequest.m_downloader.isEnabled)
|
|
return;
|
|
|
|
QNetworkReply *reply = m_httpRequest.m_reply;
|
|
disconnect(reply, SIGNAL(readyRead()), this, SLOT(onReadOnceReplyHeader()));
|
|
|
|
QString fileName = m_httpRequest.m_downloader.fileName;
|
|
m_downloadFile.setFileName(fileName);
|
|
|
|
QIODevice::OpenMode mode = QIODevice::WriteOnly;
|
|
if (m_httpRequest.m_downloader.isSupportBreakpointDownload &&
|
|
m_httpRequest.m_downloader.enabledBreakpointDownload &&
|
|
QFile::exists(fileName))
|
|
{
|
|
mode = QIODevice::Append;
|
|
}
|
|
|
|
if (!m_downloadFile.open(mode)) {
|
|
QString error = QString("Url: %1 %2 Non-Writable")
|
|
.arg(m_httpRequest.m_request.url().toString())
|
|
.arg(m_downloadFile.fileName());
|
|
emit downloadFileError();
|
|
emit downloadFileError(error);
|
|
}
|
|
else {
|
|
// todo startDownload
|
|
}
|
|
}
|
|
|
|
void HttpResponse::onEncrypted()
|
|
{
|
|
emit encrypted();
|
|
}
|
|
|
|
void HttpResponse::onMetaDataChanged()
|
|
{
|
|
emit metaDataChanged();
|
|
}
|
|
|
|
void HttpResponse::onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
|
|
{
|
|
emit preSharedKeyAuthenticationRequired(authenticator);
|
|
}
|
|
|
|
void HttpResponse::onRedirectAllowed()
|
|
{
|
|
emit redirectAllowed();
|
|
}
|
|
|
|
void HttpResponse::onRedirected(const QUrl &url)
|
|
{
|
|
emit redirected(url);
|
|
}
|
|
|
|
void HttpResponse::onSslErrors(const QList<QSslError> &errors)
|
|
{
|
|
emit sslErrors(errors);
|
|
}
|
|
|
|
void HttpResponse::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
|
|
{
|
|
if (this->reply() != reply) {
|
|
return;
|
|
}
|
|
|
|
m_authenticationCount++;
|
|
|
|
bool isAuthenticationSuccessed = (m_authenticationCount >= 2);
|
|
if (isAuthenticationSuccessed) {
|
|
emit authenticationRequireFailed();
|
|
emit authenticationRequireFailed(this->reply());
|
|
}
|
|
|
|
if (m_httpRequest.m_authenticationRequiredCount >= 0 &&
|
|
m_authenticationCount > m_httpRequest.m_authenticationRequiredCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_httpRequest.m_authenticator.isNull()) {
|
|
emit authenticationRequired(authenticator);
|
|
}
|
|
else {
|
|
authenticator->setUser(m_httpRequest.m_authenticator.user());
|
|
authenticator->setPassword(m_httpRequest.m_authenticator.password());
|
|
// todo setOption....
|
|
}
|
|
}
|
|
|
|
void HttpResponse::onHandleHead()
|
|
{
|
|
if (m_isHandleHead) {
|
|
return;
|
|
}
|
|
|
|
m_isHandleHead = true;
|
|
|
|
QNetworkReply *reply = m_httpRequest.m_reply;
|
|
if (this->receivers(SIGNAL(head(QList<QNetworkReply::RawHeaderPair>))) ||
|
|
this->receivers(SIGNAL(head(QMap<QString, QString>)))
|
|
)
|
|
{
|
|
emit head(reply->rawHeaderPairs());
|
|
QMap<QString, QString> map;
|
|
foreach (auto each, reply->rawHeaderPairs()) {
|
|
map[each.first] = each.second;
|
|
}
|
|
|
|
emit head(map);
|
|
}
|
|
}
|
|
|
|
inline QString lineIndent(const QString &source, const QString &indentString)
|
|
{
|
|
QRegularExpression rx("^(.*)");
|
|
QRegularExpression::PatternOptions patternOptions;
|
|
patternOptions |= QRegularExpression::MultilineOption;
|
|
rx.setPatternOptions(patternOptions);
|
|
return QString(source).replace(rx, indentString + "\\1");
|
|
}
|
|
|
|
inline QString networkHeader2String(const QNetworkRequest &request)
|
|
{
|
|
QString headerString;
|
|
for (const QByteArray &each : request.rawHeaderList()) {
|
|
QByteArray value = request.rawHeader(each);
|
|
headerString += QString("%1: %2\n").arg(QString(each)).arg(QString(value));
|
|
}
|
|
|
|
if (headerString.isEmpty()) {
|
|
return "null";
|
|
}
|
|
|
|
if (headerString.at(headerString.count()-1) == '\n') {
|
|
headerString.chop(1);
|
|
}
|
|
|
|
return headerString;
|
|
}
|
|
|
|
inline QString networkReplyHeader2String(const QNetworkReply *reply)
|
|
{
|
|
QString headerString;
|
|
for (const QByteArray &each : reply->rawHeaderList()) {
|
|
QByteArray value = reply->rawHeader(each);
|
|
headerString += QString("%1: %2\n").arg(QString(each)).arg(QString(value));
|
|
}
|
|
|
|
if (headerString.isEmpty()) {
|
|
return "null";
|
|
}
|
|
|
|
if (headerString.at(headerString.count()-1) == '\n') {
|
|
headerString.chop(1);
|
|
}
|
|
|
|
return headerString;
|
|
}
|
|
|
|
inline QString networkBodyType2String(HttpRequest::BodyType t)
|
|
{
|
|
if (t == HttpRequest::MultiPart) {
|
|
return "MultiPart";
|
|
}
|
|
else if (t == HttpRequest::FileMap) {
|
|
return "FileMap";
|
|
}
|
|
else if (t == HttpRequest::FormData) {
|
|
return "FormData";
|
|
}
|
|
else if (t == HttpRequest::Raw) {
|
|
return "Raw";
|
|
}
|
|
else if (t == HttpRequest::Raw_Json) {
|
|
return "RawJson";
|
|
}
|
|
else if (t == HttpRequest::X_Www_Form_Urlencoded) {
|
|
return "x_www_form_urlencoded";
|
|
}
|
|
else {
|
|
return "None";
|
|
}
|
|
}
|
|
|
|
inline QString networkBody2String(const QPair<HttpRequest::BodyType, QVariant> &body)
|
|
{
|
|
QString bodyTypeString;
|
|
bodyTypeString += "Type: " + networkBodyType2String(body.first) + "\n";
|
|
bodyTypeString += "Data: \n";
|
|
|
|
QString bodyDataString;
|
|
if (body.first == HttpRequest::MultiPart) {
|
|
QDebug d(&bodyDataString);
|
|
d << body.second;
|
|
}
|
|
else if (body.first == HttpRequest::FileMap) {
|
|
const auto &fileMap = body.second.value<QMap<QString, QString>>();
|
|
for (const auto &each : fileMap.toStdMap()) {
|
|
const QString &key = each.first;
|
|
const QString &filePath = each.second;
|
|
bodyDataString += key + ": " + filePath + "\n";
|
|
}
|
|
}
|
|
else if (body.first == HttpRequest::FormData) {
|
|
const auto &formDataMap = body.second.value<QMap<QString, QVariant>>();
|
|
for (const auto &each : formDataMap.toStdMap()) {
|
|
const QString &key = each.first;
|
|
const QString &value = each.second.toString();
|
|
bodyDataString += key + ": " + value + "\n";
|
|
}
|
|
}
|
|
else if (body.first == HttpRequest::X_Www_Form_Urlencoded ||
|
|
body.first == HttpRequest::Raw ||
|
|
body.first == HttpRequest::Raw_Json)
|
|
{
|
|
bodyDataString += body.second.toByteArray();
|
|
}
|
|
|
|
if (bodyDataString.isEmpty()) {
|
|
bodyDataString = "null";
|
|
}
|
|
|
|
bodyDataString = lineIndent(bodyDataString, "=> ");
|
|
|
|
QString bodyString = bodyTypeString + bodyDataString;
|
|
if (bodyString.at(bodyString.count()-1) == '\n') {
|
|
bodyString.chop(1);
|
|
}
|
|
|
|
return bodyString;
|
|
}
|
|
|
|
inline QString networkOperation2String(QNetworkAccessManager::Operation o)
|
|
{
|
|
static QMap<QNetworkAccessManager::Operation, QByteArray> verbMap =
|
|
{
|
|
{QNetworkAccessManager::HeadOperation, "HEAD"},
|
|
{QNetworkAccessManager::GetOperation, "GET"},
|
|
{QNetworkAccessManager::PostOperation, "POST"},
|
|
{QNetworkAccessManager::PutOperation, "PUT"},
|
|
{QNetworkAccessManager::DeleteOperation, "DELETE"},
|
|
};
|
|
|
|
return verbMap.value(o, "");
|
|
}
|
|
}
|
|
|
|
#define HTTPRESPONSE_DECLARE_METATYPE(...) \
|
|
Q_DECLARE_METATYPE(std::function<void (__VA_ARGS__)>)
|
|
|
|
HTTPRESPONSE_DECLARE_METATYPE(void)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QByteArray)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QString)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QVariantMap)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QJsonObject)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QNetworkReply*)
|
|
HTTPRESPONSE_DECLARE_METATYPE(qint64, qint64)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QNetworkReply::NetworkError)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator*)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QUrl)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QList<QSslError>)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QAuthenticator*)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QList<QNetworkReply::RawHeaderPair>)
|
|
HTTPRESPONSE_DECLARE_METATYPE(QMap<QString, QString>)
|
|
|
|
#endif // QTHUB_COM_HTTPCLIENT_HPP
|