QtNetworkService
QtNetworkService copied to clipboard
版本兼容问题
qt版本6.3,c++17,里面有很多报错,可以兼容下吗?表示特别感谢!
#ifndef QTHUB_COM_HTTPCLIENT_HPP
#define QTHUB_COM_HTTPCLIENT_HPP
#include <QRegularExpression>
#include <QNetworkRequest>
#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 <QRegExp>
#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() const;
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 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 (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 (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(QVariantMap resultMap);
void downloadProgress(qint64, qint64);
void uploadProgress(qint64, qint64);
void error(QByteArray 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) :
QObject(parent),
m_httpRequest(httpRequest)
{
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()
{
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::FollowRedirectsAttribute, true)
#else
.attribute(QNetworkRequest::RedirectPolicyAttribute, true)
#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
QRegExp rx("attachment;\\s*filename=([\\S]+)");
if (rx.exactMatch(dispositionHeader)) {
m_fileName = rx.cap(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(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, bool isBlock) : QEventLoop(reply)
{
if (isBlock) {
connect(reply, SIGNAL(finished()), this, SLOT(quit()));
this->exec();
}
}
};
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#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);
#else
#define _logger(l1, l2, str) \
do { \
if (l1 >= l2) { \
if (l2 >= HttpRequest::Debug) { \
qDebug() << "D:" << str; \
} \
else if (l2 == HttpRequest::Warn) { \
qDebug() << "W:" << str; \
} \
else if (l2 == HttpRequest::Error) { \
qDebug() << "E:" << str; \
} \
else if (l2 == HttpRequest::Fatal) { \
qDebug() << "F:" << str; \
} \
} \
} while(0);
#endif
#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 (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 (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, QString("Http reply invalid"));
Q_ASSERT(httpRequest.m_reply);
return nullptr;
}
// 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)
{
}
QString HttpClient::getVersion() const
{
return "1.1.0";
}
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::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()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
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, QVariantMap);
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, 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));
}
}
}
}
if (reply && httpRequest.m_isBlock) {
new HttpBlocker(reply, httpRequest.m_isBlock);
}
HttpRequest oldRequest = m_httpRequest;
m_httpRequest = httpRequest;
if (oldRequest.m_reply != httpRequest.m_reply) {
emit replyChanged(httpRequest.m_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(QVariantMap))) > 0)
{
QByteArray result = reply->readAll();
emit finished();
emit finished(result);
QVariantMap resultMap = QJsonDocument::fromJson(result).object().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.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->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";
}
return headerString.trimmed();
}
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";
}
return headerString.trimmed();
}
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, "=> ");
return bodyTypeString + bodyDataString.trimmed();
}
inline QString networkOperation2String(QNetworkAccessManager::Operation o)
{
static QMap<QNetworkAccessManager::Operation, QByteArray> verbMap =
{
{QNetworkAccessManager::HeadOperation, "HEAD"},
{QNetworkAccessManager::GetOperation, "GET"},
{QNetworkAccessManager::PostOperation, "POST"},
{QNetworkAccessManager::PutOperation, "PUT"},
};
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(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
qt版本6.3,c++17,里面有很多报错,可以兼容下吗?表示特别感谢!
最新版本已支持。