├── LICENSE ├── QtNetworkService.pro ├── README.md ├── sample ├── Downloader │ ├── Downloader.cpp │ ├── Downloader.h │ ├── Downloader.pro │ ├── app.ico │ └── main.cpp └── main.cpp └── src ├── HttpClient.h └── QtNetworkService.pri /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Qt君 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QtNetworkService.pro: -------------------------------------------------------------------------------- 1 | #********************************************************** 2 | #* Author(作者) : Qt君 3 | #* 微信公众号 : Qt君 4 | #* Website(网站) : qthub.com 5 | #* QQ交流群 : 1039852727 6 | #* Email(邮箱) : 2088201923@qq.com 7 | #* Support(技术支持&合作) :2088201923(QQ) 8 | #* Source Code(源码): https://github.com/aeagean/QtNetworkService 9 | #* LISCENSE(开源协议): MIT 10 | #* Demo(演示): 11 | #========================================================== 12 | # static AeaQt::HttpClient client; 13 | # client.get("https://qthub.com") 14 | # .onSuccess([](QString result) { qDebug()<<"success!"; }) 15 | # .onFailed([](QString error) { qDebug()<<"failed!"; }) 16 | # .exec(); 17 | #========================================================== 18 | #*********************************************************/ 19 | QT += core 20 | QT -= gui 21 | 22 | DEFINES += QT_APP_MODE 23 | 24 | contains(DEFINES, QT_APP_MODE) { 25 | SOURCES += sample/main.cpp 26 | 27 | message(" ================ QtNetworkService Application ================ ") 28 | } 29 | else { 30 | CONFIG += staticlib 31 | TEMPLATE = lib 32 | unix: TARGET = $$PWD/lib/QtNetworkService 33 | 34 | win32: { 35 | DESTDIR = $$PWD/Lib/ 36 | TARGET = QtNetworkService 37 | } 38 | 39 | message(" ================ QtNetworkService Library ================ ") 40 | } 41 | 42 | msvc { 43 | QMAKE_CFLAGS += /utf-8 44 | QMAKE_CXXFLAGS += /utf-8 45 | } 46 | 47 | include($$PWD/src/QtNetworkService.pri) 48 | 49 | DISTFILES += \ 50 | README.md 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 一个简单的使用例子 2 | ```cpp 3 | HttpClient client; 4 | client.get("https://qthub.com") 5 | .onSuccess([](QString result) { qDebug()<<"result:"< lambda); 21 | HttpRequest &onSuccess(std::function lambda); 22 | HttpRequest &onSuccess(std::function lambda); 23 | ``` 24 | 25 | * Http请求返回失败的信号槽绑定 26 | ```cpp 27 | HttpRequest &onFailed(const QObject *receiver, const char *method); 28 | HttpRequest &onFailed(std::function lambda); 29 | HttpRequest &onFailed(std::function lambda); 30 | HttpRequest &onFailed(std::function lambda); 31 | ``` 32 | 33 | ### 例子: 34 | ```cpp 35 | static HttpClient client; 36 | client.get("https://qthub.com") 37 | .onSuccess(this, SLOT(onSuccess(QString))) 38 | .onFailed(this, SLOT(onFailed(QString))) 39 | .exec(); // 执行Http操作 40 | ``` 41 | 42 | ## 2.2 使用匿名函数的方式实现成功与失败的事件处理 43 | ### 接口: 44 | * Http请求返回成功的回调事件 45 | ```cpp 46 | HttpRequest &onSuccess(std::function lambda); 47 | HttpRequest &onSuccess(std::function lambda); 48 | HttpRequest &onSuccess(std::function lambda); 49 | ``` 50 | 51 | * Http请求返回失败的回调事件 52 | ```cpp 53 | HttpRequest &onFailed(std::function lambda); 54 | HttpRequest &onFailed(std::function lambda); 55 | HttpRequest &onFailed(std::function lambda); 56 | ``` 57 | 58 | ### 例子: 59 | ```cpp 60 | client.get("https://qthub.com") 61 | .onSuccess([](QString result) { qDebug()<<"result:"< lambda); 86 | ``` 87 | 88 | ### 例子: 89 | ```cpp 90 | client.get("https://qthub.com") 91 | .onSuccess([](QString result) { qDebug()<<"result: " << result.left(10); }) 92 | .onDownloadProgress([](qint64 bytesReceived, qint64 bytesTotal) { 93 | qDebug() << "lambda bytes received: " << bytesReceived 94 | << "bytes total: " << bytesTotal; 95 | }) 96 | .onFailed([](QString error) { qDebug()<<"error: " << error; }) 97 | .exec(); 98 | ``` 99 | 100 | ## 2.5 post 上传文件并获取上传进度 101 | 102 | ### 接口: 103 | ```cpp 104 | HttpRequest &onUploadProgress(const QObject *receiver, const char *method); 105 | HttpRequest &onUploadProgress(std::function lambda); 106 | ``` 107 | 108 | ### 例子: 109 | ```cpp 110 | client.post("http://httpbin.org/post") 111 | .bodyWithFile("text_file", "helloworld.txt") 112 | .onUploadProgress([](qint64 bytesSent, qint64 bytesTotal) { 113 | qDebug() << "lambda bytes sent: " << bytesSent 114 | << "bytes total: " << bytesTotal; 115 | }) 116 | .onSuccess([](QString result) { qDebug()<<"result: " << result.left(100); }) 117 | .onFailed([](QString error) { qDebug()<<"error: " << error; }) 118 | .exec(); 119 | ``` 120 | 121 | ## 2.6 自定义超时时间和超时处理 122 | 123 | - timeout(ms)是设置超时时间,单位为毫秒(ms)。 124 | - onTimeout 为超时回调,当超时事件触发,自动调用 onTimeout 回调。 125 | 126 | ### 接口: 127 | * 设置超时时间 128 | ```cpp 129 | HttpRequest &timeout(const int &msec = -1); 130 | ``` 131 | 132 | * 设置超时的回调函数 133 | ```cpp 134 | HttpRequest &onTimeout(const QObject *receiver, const char *method); 135 | HttpRequest &onTimeout(std::function lambda); 136 | HttpRequest &onTimeout(std::function lambda); 137 | ``` 138 | 139 | ### 例子: 140 | ```cpp 141 | client.get("https://qthub.com") 142 | .onSuccess([](QString result) { qDebug()<<"result:"< &headers); 173 | ``` 174 | 175 | ### 例子: 176 | 177 | ```cpp 178 | client.post("https://example.com") 179 | .header("content-type", "application/json") 180 | .queryParam("key", "Hello world!") 181 | .body(R"({"user": "test"})") 182 | .onSuccess([](QString result){}) 183 | .onFailed([](QString error){}) 184 | .exec(); 185 | ``` 186 | 187 | ## 2.9 添加 params 188 | 189 | ### 接口: 190 | 191 | ```cpp 192 | HttpRequest &queryParam(const QString &key, const QVariant &value); 193 | HttpRequest &queryParams(const QMap ¶ms); 194 | ``` 195 | 196 | ### 例子: 197 | 198 | ```cpp 199 | client.get("https://example.com") 200 | .queryParam("key1", "value1") 201 | .queryParam("key2", "value2") 202 | .queryParam("key3", "value3") 203 | .onSuccess([](QString result){}) 204 | .onFailed([](QString error){}) 205 | .exec(); 206 | ``` 207 | 208 | 上面代码等同于: 209 | 210 | ```cpp 211 | client.get("https://example.com?key1=value1&key2=value2&key3=value3") 212 | .onSuccess([](QString result){}) 213 | .onFailed([](QString error){}) 214 | ``` 215 | 216 | ## 2.10 添加 body 217 | 218 | ### 接口: 219 | 220 | - 原始数据 221 | 222 | ```cpp 223 | HttpRequest &body(const QByteArray &raw); 224 | HttpRequest &bodyWithRaw(const QByteArray &raw); 225 | ``` 226 | 227 | - json 数据 228 | 229 | ```cpp 230 | HttpRequest &body(const QJsonObject &json); 231 | HttpRequest &bodyWithJson(const QJsonObject &json); 232 | ``` 233 | 234 | - 表单数据 235 | 236 | ```cpp 237 | HttpRequest &body(const QVariantMap &formUrlencodedMap); 238 | HttpRequest &bodyWithFormUrlencoded(const QVariantMap &keyValueMap); 239 | ``` 240 | 241 | - 混合消息 242 | 243 | ```cpp 244 | HttpRequest &body(QHttpMultiPart *multiPart); 245 | HttpRequest &bodyWithMultiPart(QHttpMultiPart *multiPart); 246 | ``` 247 | 248 | - 文件消息 249 | 250 | ```cpp 251 | HttpRequest &body(const QString &key, const QString &file); 252 | HttpRequest &bodyWithFile(const QString &key, const QString &file); 253 | HttpRequest &bodyWithFile(const QMap &fileMap); 254 | ``` 255 | 256 | ### 例子: 257 | 258 | #### 发送原始数据 259 | 260 | ```cpp 261 | client.post("http://httpbin.org/post") 262 | .body("hello world") 263 | .onSuccess([](QString result){qDebug()<open(QIODevice::ReadOnly); 307 | file->setParent(multiPart); 308 | 309 | QString dispositionHeader = QString("form-data; name=\"%1\";filename=\"%2\"") 310 | .arg("text_file") 311 | .arg(file->fileName()); 312 | 313 | QHttpPart part; 314 | part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); 315 | part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader); 316 | part.setBodyDevice(file); 317 | 318 | multiPart->append(part); 319 | 320 | QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data()); 321 | 322 | HttpClient client; 323 | client.post("http://httpbin.org/post") 324 | .header("content-type", contentType) 325 | .body(multiPart) 326 | .onSuccess([](QString result){ qDebug()<request().attribute(QNetworkRequest::User); 358 | qDebug()<< value.toString(); 359 | }) 360 | .onFailed([](QString error){ qDebug()< lambda); 381 | HttpRequest &onDownloadFileSuccess(std::function lambda); 382 | 383 | HttpRequest &onDownloadFileFailed(const QObject *receiver, const char *method); 384 | HttpRequest &onDownloadFileFailed(std::function lambda); 385 | HttpRequest &onDownloadFileFailed(std::function lambda); 386 | ``` 387 | 388 | 3. 断点续传下载 389 | 390 |   默认开启断点续传下载功能,即当程序异常退出时,再次运行会接着从已下载的位置下载。 391 |   需要注意的是: 如果服务器不支持断点续传功能,则每次下载都是从头开始下载。 392 | 393 | 参数 | 解释 394 | --- | --- 395 | enabled | 开启/关闭断点续传下载 396 | 397 | ```cpp 398 | HttpRequest &enabledBreakpointDownload(bool enabled = true); 399 | ``` 400 | 401 | 4. 文件下载进度的响应回调/信号槽 402 | 403 |   回调传递的参数分别是已保存的文件字节数和文件总字节大小。 404 | ```cpp 405 | HttpRequest &onDownloadFileProgress(const QObject *receiver, const char *method); 406 | HttpRequest &onDownloadFileProgress(std::function lambda); 407 | ``` 408 | 409 | ### 例子: 410 | 1. 简单下载文件 411 | ```cpp 412 | client.get("https://hub.fastgit.org/aeagean/QtNetworkService/archive/refs/heads/master.zip") 413 | .download("QtNetworkService.zip") 414 | .onDownloadFileSuccess([](QString fileName) { 415 | qDebug() << "Download completed: " << fileName; 416 | }) 417 | .onDownloadFileFailed([](QString error) { 418 | qDebug() << "Download failed: " << error; 419 | }) 420 | .exec(); 421 | ``` 422 | 423 | 2. 断点续传下载 424 | ```cpp 425 | client.get("http://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/6.0/6.0.3/single/qt-everywhere-src-6.0.3.tar.xz") 426 | .download() // 启用自动设置文件名字 => qt-everywhere-src-6.0.3.tar.xz 427 | .enabledBreakpointDownload() // 启用断点续传下载 428 | .onDownloadFileProgress([](qint64 recv, qint64 total) { 429 | // 获取文件下载进度 430 | qDebug().nospace() << (100 * qreal(recv)/total) << "%"; 431 | }) 432 | .onDownloadFileSuccess([](QString fileName) { 433 | // 下载成功 434 | qDebug() << "Download completed: " << fileName; 435 | }) 436 | .onDownloadFileFailed([](QString error) { 437 | // 下载失败 438 | qDebug() << "Download failed: " << error; 439 | }) 440 | .onSuccess([](QString result) { // 可省略 441 | qDebug() << "success: " << result; 442 | }) 443 | .onFailed([](QString err) { // 可省略 444 | qDebug() << "failed: " << err; 445 | }) 446 | .exec(); 447 | ``` 448 | 449 | ## 2.13 失败重试 450 | ### 接口: 451 |   设置失败请求后的重试次数,默认值为0。 452 | ```cpp 453 | HttpRequest &retry(int count); 454 | ``` 455 | 456 |   重试次数执行完成后的信号槽/回调。 457 | ```cpp 458 | HttpRequest &onRetried(const QObject *receiver, const char *method); 459 | HttpRequest &onRetried(std::function lambda); 460 | ``` 461 | 462 | ### 例子: 463 | ```cpp 464 | client.get("xxx://httpbin.org/get") 465 | .retry(2) // 失败重试的次数 466 | .onRetried([](){qDebug()<<"retried!";}) // 失败重试操作完成后的回调 467 | .onSuccess([](QString result){qDebug()< lambda); 483 | ``` 484 | 485 | ### 例子: 486 | ```cpp 487 | client.get("https://httpbin.org/get") 488 | .repeat(3) // 总共重复请求的次数 489 | .onRepeated([](){qDebug()<<"repeated!";}) // 重复请求操作完成后的回调 490 | .onSuccess([](QString result){qDebug()< lambda); 507 | ``` 508 | 509 | 3. 设置限制身份验证的次数,超过身份验证计数则触发失败并中断请求。 510 | 511 | 变量 | 解释 512 | --- | --- 513 | `count < 0` | 不限制验证次数 514 | `count = 0` | 不验证 515 | `count = 1` | 限制验证1次(默认值) 516 | `count > 0` | 限制count次 517 | 518 | ```cpp 519 | inline HttpRequest &authenticationRequiredCount(int count = 1); 520 | ``` 521 | 522 | 4. 身份验证失败后错误回调。 523 | ```cpp 524 | inline HttpRequest &onAuthenticationRequireFailed(const QObject *receiver, const char *method); 525 | inline HttpRequest &onAuthenticationRequireFailed(std::function lambda); 526 | inline HttpRequest &onAuthenticationRequireFailed(std::function lambda); 527 | ``` 528 | 529 | ### 例子 530 | 1. 身份验证 531 | ```cpp 532 | client.get("https://httpbin.org/basic-auth/admin/123456") 533 | .onAuthenticationRequired([](QAuthenticator *authenticator) { 534 | authenticator->setUser("admin"); 535 | authenticator->setPassword("123456"); 536 | qDebug() << "============="; 537 | }) 538 | .onSuccess([](QString result){qDebug()<<"success: "<setUser("admin"); 558 | authenticator->setPassword("1234563"); // 错误输入 559 | }) 560 | .onAuthenticationRequireFailed([](){ // 验证身份失败的回调 561 | qDebug() << "authentication failed!"; 562 | }) 563 | .onSuccess([](QString result){qDebug()<<"success: "< 595 | 微信公众号:Qt君 596 |

Qt君

597 |

598 | 599 | # 源码地址 600 | https://github.com/aeagean/QtNetworkService 601 | -------------------------------------------------------------------------------- /sample/Downloader/Downloader.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************** 2 | * Author(作者) : Qt君 3 | * 微信公众号 : Qt君 4 | * Website(网站) : qthub.com 5 | * QQ交流群 : 1039852727 6 | * Email(邮箱) : 2088201923@qq.com 7 | * Support(技术支持&合作) :2088201923(QQ) 8 | * Source Code(源码): https://github.com/aeagean/QtNetworkService 9 | * LISCENSE(开源协议): MIT 10 | **********************************************************/ 11 | #include "Downloader.h" 12 | 13 | #include 14 | #include 15 | 16 | using namespace AeaQt; 17 | 18 | #define KB (1024 * 1) 19 | #define MB (1024 * KB) 20 | #define GB (1024 * MB) 21 | 22 | QString toBytes(qint64 v) 23 | { 24 | if (v < MB) { 25 | return QString::number(v/KB) + "KB"; 26 | } 27 | else if (v >= MB && v < GB) { 28 | return QString::number(qreal(v)/MB, 'f', 2) + "MB"; 29 | } 30 | else { 31 | return QString::number(qreal(v)/GB, 'f', 2) + "GB"; 32 | } 33 | } 34 | 35 | Downloader::Downloader(QWidget *parent) : QWidget(parent) 36 | { 37 | m_urlEdit->setText("http://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/6.0/6.0.3/single/qt-everywhere-src-6.0.3.tar.xz"); 38 | m_progressBar->setValue(0); 39 | 40 | QGridLayout *g = new QGridLayout; 41 | g->addWidget(m_urlLabel, 0, 0, 1, 1); 42 | g->addWidget(m_urlEdit, 0, 1, 1, 3); 43 | 44 | g->addWidget(m_fileNameLabel, 1, 0, 1, 1); 45 | g->addWidget(m_fileNameValueLabel, 1, 1, 1, 2); 46 | g->addWidget(m_openFileDirBtn, 1, 3, 1, 1); 47 | 48 | g->addWidget(m_progressBar, 2, 0, 1, 4); 49 | 50 | g->addWidget(m_speedLabel, 3, 0, 1, 1); 51 | g->addWidget(m_speedValueLabel, 3, 1, 1, 1); 52 | g->addWidget(m_remainTimeLabel, 3, 2, 1, 1); 53 | g->addWidget(m_remainTimeValueLabel, 3, 3, 1, 1); 54 | 55 | g->addWidget(m_currentSizeLabel, 4, 0, 1, 1); 56 | g->addWidget(m_currentSizeValueLabel, 4, 1, 1, 1); 57 | g->addWidget(m_fileSizeLabel, 4, 2, 1, 1); 58 | g->addWidget(m_fileSizeValueLabel, 4, 3, 1, 1); 59 | 60 | g->addWidget(m_statusLabel, 5, 0, 1, 1); 61 | g->addWidget(m_startBtn, 5, 1, 1, 1); 62 | g->addWidget(m_pauseBtn, 5, 2, 1, 1); 63 | g->addWidget(m_restoreBtn, 5, 3, 1, 1); 64 | 65 | this->setLayout(g); 66 | 67 | connect(m_startBtn, &QPushButton::clicked, this, &Downloader::startDownload); 68 | connect(m_pauseBtn, &QPushButton::clicked, this, &Downloader::pauseDownload); 69 | connect(m_restoreBtn, &QPushButton::clicked, this, &Downloader::restoreDownload); 70 | connect(m_openFileDirBtn, &QPushButton::clicked, this, &Downloader::onOpenFileDirClicked); 71 | 72 | connect(m_statisticsTimer, &QTimer::timeout, this, &Downloader::onStatisticsTimer); 73 | 74 | m_statisticsTimer->start(1000); 75 | } 76 | 77 | // 开始下载 78 | void Downloader::startDownload() 79 | { 80 | if (isDownloading()) { 81 | return; 82 | } 83 | 84 | m_statusLabel->setText("正在下载..."); 85 | // 开启统计下载信息定时器 86 | m_statisticsTimer->start(); 87 | 88 | // 断点续传下载文件 89 | m_response = m_client.get(m_urlEdit->text()) 90 | .download() 91 | .enabledBreakpointDownload() 92 | .onDownloadFileNameChanged(this, SLOT(onDownloadFileNameChanged(QString))) 93 | .onDownloadFileProgress(this, SLOT(onDownloadFileProgress(qint64,qint64))) 94 | .onDownloadFileSuccess(this, SLOT(onDownloadFileSuccess(QString))) 95 | .onDownloadFileFailed(this, SLOT(onDownloadFileFailed(QString))) 96 | .exec(); 97 | 98 | m_isDownloading = true; 99 | } 100 | 101 | // 暂停下载 102 | void Downloader::pauseDownload() 103 | { 104 | if (isDownloading()) { 105 | m_response->reply()->abort(); 106 | m_isDownloading = false; 107 | } 108 | 109 | m_statisticsTimer->stop(); 110 | m_statusLabel->setText("已暂停"); 111 | } 112 | 113 | // 重新下载 114 | void Downloader::restoreDownload() 115 | { 116 | this->pauseDownload(); 117 | 118 | // 移除下载的文件 119 | QFile::remove(m_fileName); 120 | this->startDownload(); 121 | } 122 | 123 | void Downloader::onOpenFileDirClicked() 124 | { 125 | QFileInfo fileInfo(m_fileName); 126 | bool ok = QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); 127 | qDebug() << "open url result: " << ok; 128 | } 129 | 130 | // 统计下载信息 131 | void Downloader::onStatisticsTimer() 132 | { 133 | if (m_lastRecvSize == -1) { 134 | m_lastRecvSize = m_currentRecvSize; 135 | return; 136 | } 137 | 138 | qint64 speed = (m_currentRecvSize - m_lastRecvSize) * 1000 / m_statisticsTimer->interval(); 139 | qint64 remainSize = m_totalSize - m_currentRecvSize; 140 | 141 | if (speed >= 0) { 142 | m_speedValueLabel->setText(toBytes(speed)); 143 | } 144 | 145 | if (speed > 0 && remainSize > 0) { 146 | m_remainTimeValueLabel->setText(QString::number(remainSize/speed) + "秒"); 147 | } 148 | else { 149 | m_remainTimeValueLabel->setText(QString::number(0) + "秒"); 150 | } 151 | 152 | m_lastRecvSize = m_currentRecvSize; 153 | } 154 | 155 | void Downloader::onDownloadFileNameChanged(QString fileName) 156 | { 157 | this->m_fileName = fileName; 158 | m_fileNameValueLabel->setText(fileName); 159 | } 160 | 161 | void Downloader::onDownloadFileProgress(qint64 recv, qint64 total) 162 | { 163 | m_currentRecvSize = recv; 164 | m_totalSize = total; 165 | m_currentSizeValueLabel->setText(toBytes(recv)); 166 | m_fileSizeValueLabel->setText(toBytes(total)); 167 | m_progressBar->setValue(100 * qreal(recv)/total); 168 | } 169 | 170 | void Downloader::onDownloadFileSuccess(QString fileName) 171 | { 172 | m_statusLabel->setText("下载完成"); 173 | m_isDownloading = false; 174 | qDebug() << "Download success: " << fileName; 175 | } 176 | 177 | void Downloader::onDownloadFileFailed(QString fileName) 178 | { 179 | m_statusLabel->setText("下载失败"); 180 | m_isDownloading = false; 181 | qDebug() << "Download failed: " << fileName; 182 | } 183 | 184 | bool Downloader::isDownloading() 185 | { 186 | return m_isDownloading; 187 | } 188 | -------------------------------------------------------------------------------- /sample/Downloader/Downloader.h: -------------------------------------------------------------------------------- 1 | /********************************************************** 2 | * Author(作者) : Qt君 3 | * 微信公众号 : Qt君 4 | * Website(网站) : qthub.com 5 | * QQ交流群 : 1039852727 6 | * Email(邮箱) : 2088201923@qq.com 7 | * Support(技术支持&合作) :2088201923(QQ) 8 | * Source Code(源码): https://github.com/aeagean/QtNetworkService 9 | * LISCENSE(开源协议): MIT 10 | **********************************************************/ 11 | #ifndef QTHUB_COM_DOWNLOADER_H 12 | #define QTHUB_COM_DOWNLOADER_H 13 | 14 | #include "HttpClient.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace AeaQt { 25 | 26 | class Downloader : public QWidget 27 | { 28 | Q_OBJECT 29 | public: 30 | explicit Downloader(QWidget *parent = nullptr); 31 | 32 | public slots: 33 | void startDownload(); 34 | void pauseDownload(); 35 | void restoreDownload(); 36 | 37 | private slots: 38 | void onOpenFileDirClicked(); 39 | void onStatisticsTimer(); 40 | void onDownloadFileNameChanged(QString fileName); 41 | void onDownloadFileProgress(qint64 recv, qint64 total); 42 | void onDownloadFileSuccess(QString fileName); 43 | void onDownloadFileFailed(QString fileName); 44 | 45 | private: 46 | bool isDownloading(); 47 | 48 | private: 49 | HttpClient m_client; 50 | HttpResponse *m_response = nullptr; 51 | 52 | QLabel *m_urlLabel = new QLabel("下载链接"); 53 | QLabel *m_statusLabel = new QLabel("已停止"); 54 | QLabel *m_speedLabel = new QLabel("下载速度"); 55 | QLabel *m_speedValueLabel = new QLabel("--"); 56 | QLabel *m_remainTimeLabel = new QLabel("剩余时间"); 57 | QLabel *m_remainTimeValueLabel = new QLabel("--"); 58 | QLabel *m_currentSizeLabel = new QLabel("已下载"); 59 | QLabel *m_currentSizeValueLabel = new QLabel("--"); 60 | QLabel *m_fileSizeLabel = new QLabel("总大小"); 61 | QLabel *m_fileSizeValueLabel = new QLabel("--"); 62 | QLabel *m_fileNameLabel = new QLabel("文件名字"); 63 | QLabel *m_fileNameValueLabel = new QLabel(""); 64 | QLineEdit *m_urlEdit = new QLineEdit(""); 65 | QPushButton *m_startBtn = new QPushButton("开始"); 66 | QPushButton *m_pauseBtn = new QPushButton("暂停"); 67 | QPushButton *m_restoreBtn = new QPushButton("重新下载"); 68 | QPushButton *m_openFileDirBtn = new QPushButton("打开目录"); 69 | QProgressBar *m_progressBar = new QProgressBar(); 70 | 71 | QTimer *m_statisticsTimer = new QTimer; 72 | QString m_fileName = ""; 73 | qint64 m_lastRecvSize = -1; 74 | qint64 m_currentRecvSize = -1; 75 | qint64 m_totalSize = -1; 76 | bool m_isDownloading = false; 77 | }; 78 | 79 | } 80 | #endif // QTHUB_COM_DOWNLOADER_H 81 | -------------------------------------------------------------------------------- /sample/Downloader/Downloader.pro: -------------------------------------------------------------------------------- 1 | #********************************************************** 2 | #* Author(作者) : Qt君 3 | #* 微信公众号 : Qt君 4 | #* Website(网站) : qthub.com 5 | #* QQ交流群 : 1039852727 6 | #* Email(邮箱) : 2088201923@qq.com 7 | #* Support(技术支持&合作) :2088201923(QQ) 8 | #* Source Code(源码): https://github.com/aeagean/QtNetworkService 9 | #* LISCENSE(开源协议): MIT 10 | #*********************************************************/ 11 | QT += gui 12 | QT += widgets 13 | 14 | CONFIG += c++11 15 | 16 | HEADERS += Downloader.h 17 | SOURCES += main.cpp \ 18 | Downloader.cpp 19 | 20 | win32: RC_ICONS=app.ico 21 | 22 | msvc { 23 | QMAKE_CFLAGS += /utf-8 24 | QMAKE_CXXFLAGS += /utf-8 25 | } 26 | 27 | include($$PWD/../../src/QtNetworkService.pri) 28 | -------------------------------------------------------------------------------- /sample/Downloader/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeagean/QtNetworkService/731528a1faa2e798df49cbe8b3ad1502895462d9/sample/Downloader/app.ico -------------------------------------------------------------------------------- /sample/Downloader/main.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************** 2 | * Author(作者) : Qt君 3 | * 微信公众号 : Qt君 4 | * Website(网站) : qthub.com 5 | * QQ交流群 : 1039852727 6 | * Email(邮箱) : 2088201923@qq.com 7 | * Support(技术支持&合作) :2088201923(QQ) 8 | * Source Code(源码): https://github.com/aeagean/QtNetworkService 9 | * LISCENSE(开源协议): MIT 10 | **********************************************************/ 11 | #include "Downloader.h" 12 | #include 13 | #include 14 | 15 | using namespace AeaQt; 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | QApplication a(argc, argv); 20 | 21 | Downloader d; 22 | d.setWindowTitle("断点续传下载器@Qt君"); 23 | d.show(); 24 | 25 | return a.exec(); 26 | } 27 | -------------------------------------------------------------------------------- /sample/main.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************** 2 | * Author(作者) : Qt君 3 | * 微信公众号 : Qt君 4 | * Website(网站) : qthub.com 5 | * QQ交流群 : 1039852727 6 | * Email(邮箱) : 2088201923@qq.com 7 | * Support(技术支持&合作) :2088201923(QQ) 8 | * Source Code(源码): https://github.com/aeagean/QtNetworkService 9 | * LISCENSE(开源协议): MIT 10 | * Demo(演示): 11 | ========================================================== 12 | static AeaQt::HttpClient client; 13 | client.get("https://qthub.com") 14 | .onSuccess([](QString result) { qDebug()<<"success!"; }) 15 | .onFailed([](QString error) { qDebug()<<"failed!"; }) 16 | .exec(); 17 | ========================================================== 18 | **********************************************************/ 19 | #include "HttpClient.h" 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace AeaQt; 25 | 26 | class Object : public QObject 27 | { 28 | Q_OBJECT 29 | public: 30 | Object() 31 | { 32 | } 33 | 34 | void exec() 35 | { 36 | static HttpClient client; 37 | // [0] 使用信号槽方式实现简单的监听成功与失败事件处理 38 | client.get("https://qthub.com") 39 | .onSuccess(this, SLOT(onSuccess(QString))) 40 | .onFailed(this, SLOT(onFailed(QString))) 41 | .exec(); 42 | // [0] 43 | 44 | // [1] 使用lambda方式实现简单的监听成功与失败的事件处理 45 | client.get("https://qthub.com") 46 | .onSuccess([](QString result) { qDebug()<<"result: " << result.left(10); }) 47 | .onFailed([](QString error) { qDebug()<<"error: " << error; }) 48 | .exec(); 49 | // [1] 50 | 51 | // [2] 使用信号槽获取下载进度 52 | client.get("https://qthub.com") 53 | .onSuccess(this, SLOT(onSuccess(QString))) 54 | .onDownloadProgress(this, SLOT(onDownloadProgress(qint64, qint64))) 55 | .onFailed(this, SLOT(onFailed(QString))) 56 | .exec(); 57 | // [2] 58 | 59 | // [3] lambda获取下载进度 60 | client.get("https://qthub.com") 61 | .onSuccess([](QString result) { qDebug()<<"result: " << result.left(10); }) 62 | .onDownloadProgress([](qint64 bytesReceived, qint64 bytesTotal) { 63 | qDebug() << "lambda bytes received: " << bytesReceived 64 | << "bytes total: " << bytesTotal; 65 | }) 66 | .onFailed([](QString error) { qDebug()<<"error: " << error; }) 67 | .exec(); 68 | // [3] 69 | 70 | // [4] 自定义超时处理 71 | client.get("https://qthub.com") 72 | .onSuccess([](QString result) { qDebug()<<"result: " << result.left(10); }) 73 | .onDownloadProgress([](qint64 bytesReceived, qint64 bytesTotal) { 74 | qDebug() << "lambda bytes received: " << bytesReceived 75 | << "bytes total: " << bytesTotal; 76 | }) 77 | .onFailed([](QString error) { qDebug()<<"error: " << error; }) 78 | .onTimeout([](QNetworkReply *) { qDebug()<<"timeout"; }) 79 | .timeout(1) // 1s超时, => timeoutMs(1000) 80 | .exec(); 81 | // [4] 82 | 83 | // [5] 使用body 84 | QVariantMap map; 85 | map["&^="] = "1234"; 86 | client.post("http://localhost:1234/post") 87 | .body(map) 88 | .onSuccess(this, SLOT(onSuccess(QString))) 89 | .onFailed(this, SLOT(onFailed(QString))) 90 | .exec(); 91 | // [5] 92 | 93 | // [6] post文件 94 | client.post("http://httpbin.org/post") 95 | .bodyWithFile("text_file", "helloworld.txt") 96 | .bodyWithFile("image_file", "qt.jpg") 97 | .onUploadProgress([](qint64 bytesSent, qint64 bytesTotal) { 98 | qDebug() << "lambda bytes sent: " << bytesSent 99 | << "bytes total: " << bytesTotal; 100 | }) 101 | .onSuccess([](QString result) { qDebug()<<"result: " << result.left(100); }) 102 | .onFailed([](QString error) { qDebug()<<"error: " << error; }) 103 | .exec(); 104 | // [6] 105 | 106 | // [7] readyRead测试 107 | client.get("https://github.com/aeagean/QtNetworkService/archive/refs/heads/master.zip") 108 | .onReadyRead([](QNetworkReply *reply) { qDebug()<< "readyRead: "<readAll().size(); }) 109 | .onSuccess([](QString result) { qDebug()<<"result: success"<boundary().data()); 117 | 118 | QFile *file = new QFile("demo.txt"); 119 | file->open(QIODevice::ReadOnly); 120 | file->setParent(multiPart); 121 | 122 | QString dispositionHeader = QString("form-data; name=\"%1\";filename=\"%2\"") 123 | .arg("text_file") 124 | .arg(file->fileName()); 125 | 126 | QHttpPart part; 127 | part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); 128 | part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader); 129 | part.setBodyDevice(file); 130 | 131 | multiPart->append(part); 132 | 133 | client.post("http://httpbin.org/post") 134 | .header("content-type", contentType) 135 | .body(multiPart) 136 | .onSuccess([](QString result){ qDebug()<<"result: success"<request().attribute(QNetworkRequest::User); 146 | qDebug()<< value.toString(); 147 | }) 148 | .onFailed([](QString error){ qDebug()<setUser("admin"); 210 | authenticator->setPassword("123456"); 211 | qDebug() << "============="; 212 | }) 213 | .onSuccess([](QString result){qDebug()<<"success: "<setUser("admin"); 231 | authenticator->setPassword("1234563"); // failed 232 | }) 233 | .onAuthenticationRequireFailed([](){ // 验证身份失败的回调 234 | qDebug() << "authentication failed!"; 235 | }) 236 | .onSuccess([](QString result){qDebug()<<"success: "< qt-everywhere-src-6.0.3.tar.xz 244 | .enabledBreakpointDownload() // 启用断点续传下载 245 | .onDownloadFileProgress([](qint64 recv, qint64 total) { 246 | qDebug() << (100 * qreal(recv)/total) << "%"; 247 | }) 248 | .onDownloadFileSuccess([](QString fileName) { 249 | qDebug() << "download completed: " << fileName; 250 | }) 251 | .onSuccess([](QString result) { 252 | qDebug() << "success: " << result; 253 | }) 254 | .onFailed([](QString err) { 255 | qDebug() << "failed: " << err; 256 | }) 257 | .exec(); 258 | // [x] 259 | } 260 | 261 | public slots: 262 | // http成功返回 263 | void onSuccess(QString result) 264 | { 265 | qDebug() << "result: " << result.left(10); 266 | } 267 | 268 | void onSuccess(QNetworkReply *result) 269 | { 270 | qDebug() << "result: " << result->readAll(); 271 | } 272 | 273 | // http失败返回 274 | void onFailed(QString errorString) 275 | { 276 | qDebug() << "error string: " << errorString; 277 | } 278 | 279 | // http下载进度 280 | void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) 281 | { 282 | qDebug() << "signal-slot bytes received: " << bytesReceived 283 | << "bytes total: " << bytesTotal; 284 | } 285 | 286 | void onTimeout() 287 | { 288 | qDebug() << "timeout" << __LINE__; 289 | } 290 | 291 | void onTimeout(QNetworkReply *reply) 292 | { 293 | qDebug() << "timeout" << __LINE__; 294 | reply->deleteLater(); 295 | } 296 | 297 | private: 298 | HttpClient m_httpClient; 299 | }; 300 | 301 | void autoDeleteDemo() 302 | { 303 | HttpClient *client = new HttpClient; 304 | client->get("http://www.qthub.com") 305 | .onSuccess([](const QString &s) { qDebug() << "success: " << s.left(10); }) 306 | .onFailed([](const QString &s) { qDebug() << "error: " << s.left(10); }) 307 | .exec(); 308 | 309 | QObject::connect(client, &HttpClient::finished, client, &HttpClient::deleteLater); 310 | } 311 | 312 | void downloaderDemo() 313 | { 314 | QElapsedTimer t; 315 | t.start(); 316 | 317 | HttpClient *client = new HttpClient; 318 | HttpResponse *response = client->get("http://www.qthub.com") 319 | .header("1", 2) 320 | .download() 321 | .onDownloadFileSuccess([](QString file) { qDebug()<boundary().data()); 334 | 335 | QFile *file = new QFile("demo.txt"); 336 | file->open(QIODevice::ReadOnly); 337 | file->setParent(multiPart); 338 | 339 | QString dispositionHeader = QString("form-data; name=\"%1\";filename=\"%2\"") 340 | .arg("text_file") 341 | .arg(file->fileName()); 342 | 343 | QHttpPart part; 344 | part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); 345 | part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader); 346 | part.setBodyDevice(file); 347 | 348 | multiPart->append(part); 349 | 350 | QJsonObject json = { 351 | {"1", "2"}, 352 | {"11", "2"} 353 | }; 354 | HttpClient &client = *HttpClient::instance(); 355 | client.post("http://httpbin.org/post") 356 | .header("content-type", contentType) 357 | .body(multiPart) 358 | // .body(">>>>>>>>>>>>>>>>>") 359 | // .bodyWithFile("1", "232") 360 | // .bodyWithFile("2", "232") 361 | // .logLevel(HttpRequest::Off) 362 | .logLevel(HttpRequest::Info) 363 | .onSuccess([](QString result){ qDebug().noquote()<<"result: success"< 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | namespace AeaQt { 42 | 43 | class HttpClient; 44 | class HttpRequest; 45 | class HttpResponse; 46 | 47 | class HttpClient : public QNetworkAccessManager 48 | { 49 | Q_OBJECT 50 | public: 51 | inline static HttpClient *instance(); 52 | inline HttpClient(QObject *parent = nullptr); 53 | inline QString getVersion() {return "1.1.2";} 54 | 55 | inline HttpRequest head(const QString &url); 56 | inline HttpRequest get(const QString &url); 57 | inline HttpRequest post(const QString &url); 58 | inline HttpRequest put(const QString &url); 59 | 60 | inline HttpRequest send(const QString &url, Operation op = GetOperation); 61 | 62 | private: 63 | #if (QT_VERSION < QT_VERSION_CHECK(5, 8, 0)) 64 | inline QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data); 65 | inline QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart); 66 | #endif 67 | friend class HttpRequest; 68 | }; 69 | 70 | class HttpRequest 71 | { 72 | public: 73 | enum LogLevel { Off, Fatal, Error, Warn, Debug, Info, Trace, All}; 74 | 75 | inline explicit HttpRequest(QNetworkAccessManager::Operation op, HttpClient *httpClient); 76 | inline virtual ~HttpRequest(); 77 | 78 | inline HttpRequest &url(const QString &url); 79 | 80 | inline HttpRequest &header(const QString &key, const QVariant &value); 81 | inline HttpRequest &header(QNetworkRequest::KnownHeaders header, const QVariant &value); 82 | inline HttpRequest &headers(const QMap &headers); 83 | inline HttpRequest &headers(const QMap &headers); 84 | 85 | inline HttpRequest &queryParam(const QString &key, const QVariant &value); 86 | inline HttpRequest &queryParams(const QMap ¶ms); 87 | 88 | /* Mainly used for identification */ 89 | inline HttpRequest &userAttribute(const QVariant &value); 90 | inline HttpRequest &attribute(QNetworkRequest::Attribute attribute, const QVariant &value); 91 | 92 | inline HttpRequest &body(const QByteArray &raw); 93 | inline HttpRequest &bodyWithRaw(const QByteArray &raw); 94 | 95 | inline HttpRequest &body(const QJsonObject &json); 96 | inline HttpRequest &bodyWithJson(const QJsonObject &json); 97 | 98 | inline HttpRequest &body(const QVariantMap &formUrlencodedMap); 99 | inline HttpRequest &bodyWithFormUrlencoded(const QString &key, const QVariant &value); 100 | inline HttpRequest &bodyWithFormUrlencoded(const QVariantMap &keyValueMap); 101 | 102 | inline HttpRequest &bodyWithFormData(const QString &key, const QVariant &value); 103 | inline HttpRequest &bodyWithFormData(const QVariantMap &keyValueMap); 104 | 105 | inline HttpRequest &body(QHttpMultiPart *multiPart); 106 | inline HttpRequest &bodyWithMultiPart(QHttpMultiPart *multiPart); 107 | 108 | // multi-params 109 | inline HttpRequest &body(const QString &key, const QString &file); 110 | inline HttpRequest &bodyWithFile(const QString &key, const QString &file); 111 | inline HttpRequest &bodyWithFile(const QMap &fileMap); // => QMap; like: { "key": "/home/example/car.jpeg" } 112 | 113 | inline HttpRequest &ignoreSslErrors(const QList &errors); 114 | inline HttpRequest &sslConfiguration(const QSslConfiguration &config); 115 | 116 | inline HttpRequest &priority(QNetworkRequest::Priority priority); 117 | inline HttpRequest &maximumRedirectsAllowed(int maxRedirectsAllowed); 118 | inline HttpRequest &originatingObject(QObject *object); 119 | inline HttpRequest &readBufferSize(qint64 size); 120 | 121 | // Authentication 122 | inline HttpRequest &autoAuthenticationRequired(const QAuthenticator &authenticator); 123 | inline HttpRequest &autoAuthenticationRequired(const QString &user, const QString &password); 124 | 125 | // 超过身份验证计数则触发失败并中断请求。 126 | // count >= 0 => count 127 | // count < 0 => infinite 128 | inline HttpRequest &authenticationRequiredCount(int count = 1); 129 | 130 | /** 131 | * @brief msec <= 0, disable timeout 132 | * msec > 0, enable timeout 133 | */ 134 | inline HttpRequest &timeout(const int &second = -1); 135 | inline HttpRequest &timeoutMs(const int &msec = -1); 136 | 137 | inline HttpRequest &download(); 138 | inline HttpRequest &download(const QString &file); 139 | inline HttpRequest &enabledBreakpointDownload(bool enabled = true); 140 | 141 | inline HttpRequest &retry(int count); 142 | inline HttpRequest &repeat(int count); 143 | 144 | /** 145 | * @brief Block(or sync) current thread, entering an event loop. 146 | */ 147 | inline HttpRequest &block(bool isBlock = true); 148 | inline HttpRequest &sync(bool isSync = true); 149 | 150 | inline HttpRequest &logLevel(LogLevel level = Warn); 151 | 152 | inline HttpResponse *exec(); 153 | 154 | // onFinished == onSuccess 155 | inline HttpRequest &onSuccess(const QObject *receiver, const char *method); 156 | inline HttpRequest &onSuccess(std::function lambda); 157 | inline HttpRequest &onSuccess(std::function lambda); 158 | inline HttpRequest &onSuccess(std::function lambda); 159 | inline HttpRequest &onSuccess(std::function lambda); 160 | inline HttpRequest &onSuccess(std::function lambda); 161 | 162 | // onFinished == onSuccess 163 | inline HttpRequest &onFinished(const QObject *receiver, const char *method); 164 | inline HttpRequest &onFinished(std::function lambda); 165 | inline HttpRequest &onFinished(std::function lambda); 166 | inline HttpRequest &onFinished(std::function lambda); 167 | inline HttpRequest &onFinished(std::function lambda); 168 | inline HttpRequest &onFinished(std::function lambda); 169 | 170 | // onError == onFailed 171 | inline HttpRequest &onError(const QObject *receiver, const char *method); 172 | inline HttpRequest &onError(std::function lambda); 173 | inline HttpRequest &onError(std::function lambda); 174 | inline HttpRequest &onError(std::function lambda); 175 | inline HttpRequest &onError(std::function lambda); 176 | 177 | // onError == onFailed 178 | inline HttpRequest &onFailed(const QObject *receiver, const char *method); 179 | inline HttpRequest &onFailed(std::function lambda); 180 | inline HttpRequest &onFailed(std::function lambda); 181 | inline HttpRequest &onFailed(std::function lambda); 182 | inline HttpRequest &onFailed(std::function lambda); 183 | 184 | inline HttpRequest &onReadyRead(const QObject *receiver, const char *method); 185 | inline HttpRequest &onReadyRead(std::function lambda); 186 | 187 | inline HttpRequest &onHead(const QObject *receiver, const char *method); 188 | inline HttpRequest &onHead(std::function)> lambda); 189 | inline HttpRequest &onHead(std::function)> lambda); 190 | 191 | inline HttpRequest &onTimeout(const QObject *receiver, const char *method); 192 | inline HttpRequest &onTimeout(std::function lambda); 193 | inline HttpRequest &onTimeout(std::function lambda); 194 | 195 | inline HttpRequest &onUploadProgress(const QObject *receiver, const char *method); 196 | inline HttpRequest &onUploadProgress(std::function lambda); 197 | 198 | inline HttpRequest &onDownloadProgress(const QObject *receiver, const char *method); 199 | inline HttpRequest &onDownloadProgress(std::function lambda); 200 | 201 | /// file download interface 202 | inline HttpRequest &onDownloadFileNameChanged(const QObject *receiver, const char *method); 203 | inline HttpRequest &onDownloadFileNameChanged(std::function lambda); 204 | 205 | inline HttpRequest &onDownloadFileProgress(const QObject *receiver, const char *method); 206 | inline HttpRequest &onDownloadFileProgress(std::function lambda); 207 | 208 | inline HttpRequest &onDownloadFileSuccess(const QObject *receiver, const char *method); 209 | inline HttpRequest &onDownloadFileSuccess(std::function lambda); 210 | inline HttpRequest &onDownloadFileSuccess(std::function lambda); 211 | 212 | inline HttpRequest &onDownloadFileFailed(const QObject *receiver, const char *method); 213 | inline HttpRequest &onDownloadFileFailed(std::function lambda); 214 | inline HttpRequest &onDownloadFileFailed(std::function lambda); 215 | /// file download interface 216 | 217 | inline HttpRequest &onRetried(const QObject *receiver, const char *method); 218 | inline HttpRequest &onRetried(std::function lambda); 219 | 220 | inline HttpRequest &onRepeated(const QObject *receiver, const char *method); 221 | inline HttpRequest &onRepeated(std::function lambda); 222 | 223 | inline HttpRequest &onAuthenticationRequired(const QObject *receiver, const char *method); 224 | inline HttpRequest &onAuthenticationRequired(std::function lambda); 225 | 226 | inline HttpRequest &onAuthenticationRequireFailed(const QObject *receiver, const char *method); 227 | inline HttpRequest &onAuthenticationRequireFailed(std::function lambda); 228 | inline HttpRequest &onAuthenticationRequireFailed(std::function lambda); 229 | 230 | // onResponse == onSuccess. note: DEPRECATED 231 | inline HttpRequest &onResponse(const QObject *receiver, const char *method); 232 | inline HttpRequest &onResponse(std::function lambda); 233 | inline HttpRequest &onResponse(std::function lambda); 234 | inline HttpRequest &onResponse(std::function lambda); 235 | 236 | enum HandleType { 237 | h_onFinished = 0, 238 | h_onError, 239 | h_onDownloadProgress, 240 | h_onUploadProgress, 241 | h_onTimeout, 242 | h_onReadyRead, 243 | h_onDownloadFileSuccess, 244 | h_onDownloadFileFailed, 245 | h_onEncrypted, 246 | h_onMetaDataChanged, 247 | h_onPreSharedKeyAuthenticationRequired, 248 | h_onRedirectAllowed, 249 | h_onRedirected, 250 | h_onSslErrors, 251 | h_onRetried, 252 | h_onRepeated, 253 | h_onAuthenticationRequired, 254 | h_onAuthenticationRequireFailed, 255 | h_onHead, 256 | h_onDownloadFileProgess, 257 | h_onDownloadFileNameChanged, 258 | }; 259 | 260 | enum BodyType 261 | { 262 | None = 0, // This request does not have a body. 263 | Raw, 264 | Raw_Json, // application/json 265 | X_Www_Form_Urlencoded, // x-www-form-urlencoded 266 | FileMap, // multipart/form-data 267 | MultiPart, // multipart/form-data 268 | FormData // multipart/form-data 269 | }; 270 | 271 | 272 | protected: 273 | struct Downloader 274 | { 275 | bool isEnabled; 276 | QString fileName; 277 | bool enabledBreakpointDownload; 278 | bool isSupportBreakpointDownload; 279 | qint64 currentSize; 280 | qint64 totalSize; 281 | 282 | Downloader() 283 | { 284 | isEnabled = false; 285 | fileName = ""; 286 | enabledBreakpointDownload = true; 287 | isSupportBreakpointDownload = false; 288 | totalSize = 0; 289 | currentSize = 0; 290 | } 291 | }; 292 | 293 | QNetworkAccessManager::Operation m_op; 294 | HttpClient *m_httpClient = nullptr; 295 | QNetworkReply *m_reply = nullptr; 296 | QNetworkRequest m_request; 297 | 298 | QPair m_body = qMakePair(BodyType::None, QByteArray()); 299 | int m_timeoutMs = -1; // ms 300 | bool m_isBlock = false; 301 | int m_retryCount = 0; 302 | bool m_enabledRetry = false; 303 | int m_repeatCount = 1; 304 | QAuthenticator m_authenticator; 305 | int m_authenticationRequiredCount = 1; 306 | 307 | qint64 m_readBufferSize = -1; 308 | QList m_ignoreSslErrors; 309 | Downloader m_downloader; 310 | QMap > > m_handleMap; 311 | QVariantMap m_formUrlencodedMap; 312 | QVariantMap m_formDataMap; 313 | LogLevel m_logLevel = Warn; 314 | 315 | protected: 316 | inline QString toString(); 317 | 318 | inline HttpRequest &enabledRetry(bool isEnabled); 319 | inline HttpResponse *exec(const HttpRequest &httpRequest, HttpResponse *httpResponse = nullptr); 320 | 321 | friend class HttpResponse; 322 | friend class HttpDownloader; 323 | 324 | private: 325 | inline HttpRequest() = delete; 326 | inline HttpRequest &onResponse(HandleType type, const QObject *receiver, const char *method); 327 | inline HttpRequest &onResponse(HandleType type, QVariant lambda); 328 | inline HttpRequest &onResponse(HandleType type, QString key, QVariant value); 329 | }; 330 | 331 | class HttpResponse : public QObject 332 | { 333 | Q_OBJECT 334 | public: 335 | inline explicit HttpResponse(const HttpRequest &httpRequest, QObject *parent = nullptr); 336 | inline virtual ~HttpResponse(); 337 | 338 | inline void setHttpRequest(const HttpRequest &httpRequest); 339 | inline QNetworkReply *reply() { return m_httpRequest.m_reply; } 340 | inline QString toString() const; 341 | signals: 342 | void replyChanged(QNetworkReply *reply); 343 | void finished(QNetworkReply *reply); 344 | void finished(); 345 | void finished(QByteArray result); 346 | void finished(QString result); 347 | void finished(QVariantMap resultMap); 348 | void finished(QJsonObject resultJsonObject); 349 | 350 | void downloadProgress(qint64, qint64); 351 | void uploadProgress(qint64, qint64); 352 | 353 | void error(QByteArray error); 354 | void error(QString error); 355 | void error(); 356 | void error(QNetworkReply::NetworkError error); 357 | void error(QNetworkReply *reply); 358 | 359 | void timeout(); 360 | void timeout(QNetworkReply *reply); 361 | 362 | void readyRead(QNetworkReply *reply); 363 | 364 | void downloadFileNameChanged(QString fileName); 365 | 366 | void downloadFileFinished(); 367 | void downloadFileFinished(QString file); 368 | 369 | void downloadFileError(); 370 | void downloadFileError(QString errorString); 371 | 372 | void encrypted(); 373 | void metaDataChanged(); 374 | 375 | void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator); 376 | void redirectAllowed(); 377 | void redirected(QUrl url); 378 | void sslErrors(QList errors); 379 | 380 | void retried(); 381 | void repeated(); 382 | 383 | void authenticationRequired(QAuthenticator *authentication); 384 | void authenticationRequireFailed(); 385 | void authenticationRequireFailed(QNetworkReply *); 386 | 387 | void head(QList); 388 | void head(QMap); 389 | 390 | void downloadFileProgress(qint64 bytesReceived, qint64 bytesTotal); 391 | 392 | private slots: 393 | inline void onFinished(); 394 | inline void onError(QNetworkReply::NetworkError error); 395 | inline void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); 396 | inline void onUploadProgress(qint64 bytesSent, qint64 bytesTotal); 397 | inline void onTimeout(); 398 | inline void onReadyRead(); 399 | inline void onReadOnceReplyHeader(); 400 | 401 | inline void onEncrypted(); 402 | inline void onMetaDataChanged(); 403 | inline void onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator); 404 | inline void onRedirectAllowed(); 405 | inline void onRedirected(const QUrl &url); 406 | inline void onSslErrors(const QList &errors); 407 | 408 | inline void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); 409 | 410 | inline void onHandleHead(); 411 | 412 | private: 413 | HttpRequest m_httpRequest; 414 | QFile m_downloadFile; 415 | int m_retriesRemaining = 0; 416 | int m_authenticationCount = 0; 417 | bool m_isHandleHead = false; 418 | }; 419 | 420 | inline QString lineIndent(const QString &source, const QString &indentString); 421 | inline QString networkOperation2String(QNetworkAccessManager::Operation o); 422 | inline QString networkHeader2String(const QNetworkRequest &request); 423 | inline QString networkBody2String(const QPair &body); 424 | inline QString networkReplyHeader2String(const QNetworkReply *reply); 425 | 426 | class HttpDownloader : public QObject 427 | { 428 | Q_OBJECT 429 | HttpRequest m_httpRequest; 430 | bool m_isSupportContinueDownload = false; 431 | QString m_fileName; 432 | qint64 m_contentLength = 0; 433 | QList m_headerForPair; 434 | QMap m_headerForMap; 435 | HttpResponse *m_response = nullptr; 436 | 437 | public: 438 | HttpDownloader(const HttpRequest &httpRequest, QObject *parent) : m_httpRequest(httpRequest), QObject(parent) 439 | { 440 | m_fileName = this->m_httpRequest.m_request.url().fileName(); 441 | if (m_fileName.isEmpty()) { 442 | m_fileName = this->m_httpRequest.m_request.url().host(); 443 | } 444 | } 445 | 446 | virtual ~HttpDownloader() { } 447 | 448 | HttpResponse *exec() 449 | { 450 | // todo: fixme 451 | HttpClient &client = *HttpClient::instance(); 452 | client.get(m_httpRequest.m_request.url().toString()) 453 | .timeout(30) 454 | .block(m_httpRequest.m_isBlock) 455 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 456 | .attribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy) // Qt 6 457 | #else 458 | .attribute(QNetworkRequest::FollowRedirectsAttribute, true) 459 | #endif 460 | .onHead(this, SLOT(onHead(QList))) 461 | .onHead(this, SLOT(onHead(QMap))) 462 | .onReadyRead(this, SLOT(onReadyRead(QNetworkReply*))) 463 | .onFailed(this, SLOT(onResponse(QNetworkReply::NetworkError))) 464 | .exec(); 465 | 466 | m_response = new HttpResponse(m_httpRequest, nullptr); 467 | return m_response; 468 | } 469 | 470 | private slots: 471 | void onResponse(QNetworkReply::NetworkError) 472 | { 473 | HttpResponse *response = m_httpRequest.exec(m_httpRequest, m_response); 474 | this->setParent(response); 475 | } 476 | 477 | void onHead(QList headerForPair) 478 | { 479 | m_headerForPair = headerForPair; 480 | } 481 | 482 | void onHead(QMap headerForMap) 483 | { 484 | m_headerForMap = headerForMap; 485 | for (auto each: m_headerForMap.toStdMap()) { 486 | QString key = each.first; 487 | QString value = each.second; 488 | 489 | if (key.contains("Content-Disposition", Qt::CaseInsensitive)) { 490 | QString dispositionHeader = value; 491 | // fixme rx 492 | QRegularExpression rx("attachment;\\s*filename=([\\S]+)"); 493 | QRegularExpressionMatch match = rx.match(dispositionHeader); 494 | if (match.hasMatch()) { 495 | m_fileName = match.captured(1); 496 | } 497 | } 498 | 499 | if (key.contains("Content-Length", Qt::CaseInsensitive)) { 500 | m_contentLength = value.toLongLong(); 501 | } 502 | 503 | if (key.contains("Content-Range", Qt::CaseInsensitive) || 504 | key.contains("Accept-Ranges", Qt::CaseInsensitive)) { 505 | m_isSupportContinueDownload = true; 506 | } 507 | } 508 | } 509 | 510 | void onReadyRead(QNetworkReply *reply) 511 | { 512 | HttpResponse *response = new HttpResponse(m_httpRequest, m_httpRequest.m_reply); 513 | if (m_httpRequest.m_downloader.fileName.isEmpty()) { 514 | m_httpRequest.m_downloader.fileName = m_fileName; 515 | emit response->downloadFileNameChanged(m_fileName); 516 | } 517 | 518 | m_httpRequest.m_downloader.totalSize = m_contentLength; 519 | m_httpRequest.m_downloader.isSupportBreakpointDownload = m_isSupportContinueDownload; 520 | 521 | if (m_httpRequest.m_downloader.enabledBreakpointDownload && 522 | m_httpRequest.m_downloader.isSupportBreakpointDownload) 523 | { 524 | QFile file(m_httpRequest.m_downloader.fileName); 525 | if (file.exists() && file.open(QIODevice::ReadOnly)) { 526 | m_httpRequest.m_downloader.currentSize = file.size(); 527 | 528 | if (file.size() == m_contentLength) { 529 | emit response->head(m_headerForPair); 530 | emit response->head(m_headerForMap); 531 | 532 | emit response->downloadFileProgress(m_contentLength, m_contentLength); 533 | 534 | emit response->downloadFileFinished(); 535 | emit response->downloadFileFinished(m_httpRequest.m_downloader.fileName); 536 | 537 | emit response->finished(); 538 | emit response->finished(QString("")); 539 | emit response->finished(QByteArray("")); 540 | emit response->finished(QVariantMap{}); 541 | emit response->finished(nullptr); 542 | 543 | response->deleteLater(); 544 | } 545 | else if (file.size() > m_contentLength) { 546 | file.close(); 547 | // Clear file content 548 | file.open(QIODevice::Truncate); 549 | file.close(); 550 | } 551 | else { 552 | m_httpRequest.m_request.setRawHeader("Range", QString("bytes=%1-").arg(file.size()).toUtf8()); 553 | } 554 | } 555 | } 556 | 557 | response->deleteLater(); 558 | reply->abort(); 559 | } 560 | }; 561 | 562 | class HttpResponseTimeout : public QObject 563 | { 564 | Q_OBJECT 565 | public: 566 | HttpResponseTimeout(HttpResponse *parent, const int timeout = -1) : QObject(parent) 567 | { 568 | if (timeout > 0) { 569 | QTimer::singleShot(timeout, parent, SLOT(onTimeout())); 570 | } 571 | else { 572 | // do nothing 573 | } 574 | } 575 | }; 576 | 577 | class HttpBlocker : public QEventLoop 578 | { 579 | Q_OBJECT 580 | public: 581 | HttpBlocker(QNetworkReply *reply) : QEventLoop(nullptr) 582 | { 583 | if (reply) { 584 | connect(reply, SIGNAL(finished()), this, SLOT(quit())); 585 | this->exec(); 586 | } 587 | this->deleteLater(); 588 | } 589 | }; 590 | 591 | #define _logger(l1, l2, str) \ 592 | do { \ 593 | if (l1 >= l2) { \ 594 | if (l2 >= HttpRequest::Debug) { \ 595 | qDebug().noquote() << str; \ 596 | } \ 597 | else if (l2 == HttpRequest::Warn) { \ 598 | qWarning().noquote() << str; \ 599 | } \ 600 | else if (l2 == HttpRequest::Error) { \ 601 | qCritical().noquote() << str; \ 602 | } \ 603 | else if (l2 == HttpRequest::Fatal) { \ 604 | qFatal("%s\n", str); \ 605 | } \ 606 | } \ 607 | } while(0); 608 | 609 | #define printTrace(level, str) _logger(level, HttpRequest::Trace, str) 610 | #define printInfo(level, str) _logger(level, HttpRequest::Info, str) 611 | #define printDebug(level, str) _logger(level, HttpRequest::Debug, str) 612 | #define printWarn(level, str) _logger(level, HttpRequest::Warn, str) 613 | #define printError(level, str) _logger(level, HttpRequest::Error, str) 614 | #define printFatal(level, str) _logger(level, HttpRequest::Fatal, str) 615 | 616 | HttpRequest::~HttpRequest() 617 | { 618 | } 619 | 620 | HttpRequest::HttpRequest(QNetworkAccessManager::Operation op, HttpClient *httpClient) 621 | { 622 | m_op = op; 623 | m_httpClient = httpClient; 624 | } 625 | 626 | HttpRequest &HttpRequest::url(const QString &url) 627 | { 628 | m_request.setUrl(QUrl(url)); 629 | return *this; 630 | } 631 | 632 | HttpRequest &HttpRequest::header(QNetworkRequest::KnownHeaders header, const QVariant &value) 633 | { 634 | m_request.setHeader(header, value); 635 | return *this; 636 | } 637 | 638 | HttpRequest &HttpRequest::headers(const QMap &headers) 639 | { 640 | QMapIterator iter(headers); 641 | while (iter.hasNext()) { 642 | iter.next(); 643 | header(iter.key(), iter.value()); 644 | } 645 | 646 | return *this; 647 | } 648 | 649 | HttpRequest &HttpRequest::header(const QString &key, const QVariant &value) 650 | { 651 | m_request.setRawHeader(QByteArray(key.toStdString().data()), QByteArray(value.toString().toStdString().data())); 652 | return *this; 653 | } 654 | 655 | HttpRequest &HttpRequest::headers(const QMap &headers) 656 | { 657 | QMapIterator iter(headers); 658 | while (iter.hasNext()) { 659 | iter.next(); 660 | header(iter.key(), iter.value()); 661 | } 662 | 663 | return *this; 664 | } 665 | 666 | HttpRequest &HttpRequest::body(const QVariantMap &keyValueMap) 667 | { 668 | return bodyWithFormUrlencoded(keyValueMap); 669 | } 670 | 671 | HttpRequest &HttpRequest::bodyWithFormUrlencoded(const QString &key, const QVariant &value) 672 | { 673 | QVariantMap map; 674 | map[key] = value; 675 | 676 | return bodyWithFormUrlencoded(map); 677 | } 678 | 679 | HttpRequest &HttpRequest::bodyWithFormUrlencoded(const QVariantMap &keyValueMap) 680 | { 681 | // merge map 682 | for (auto each : keyValueMap.toStdMap()) { 683 | m_formUrlencodedMap[each.first] = each.second; 684 | } 685 | 686 | QString value; 687 | QMapIterator i(m_formUrlencodedMap); 688 | while (i.hasNext()) { 689 | i.next(); 690 | 691 | value += QString("%1=%2") 692 | .arg(QUrl::toPercentEncoding(i.key()).data()) 693 | .arg(QUrl::toPercentEncoding(i.value().toString()).data()); 694 | if (i.hasNext()) { 695 | value += "&"; 696 | } 697 | } 698 | 699 | m_body = qMakePair(X_Www_Form_Urlencoded, value); 700 | m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); 701 | 702 | return *this; 703 | } 704 | 705 | HttpRequest &HttpRequest::bodyWithFormData(const QString &key, const QVariant &value) 706 | { 707 | QVariantMap map; 708 | map[key] = value; 709 | 710 | return bodyWithFormData(map); 711 | } 712 | 713 | HttpRequest &HttpRequest::bodyWithFormData(const QVariantMap &keyValueMap) 714 | { 715 | // merge map 716 | for (auto each : keyValueMap.toStdMap()) { 717 | m_formDataMap[each.first] = each.second; 718 | } 719 | 720 | m_body = qMakePair(FormData, m_formDataMap); 721 | m_request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data"); 722 | return *this; 723 | } 724 | 725 | HttpRequest &HttpRequest::body(const QJsonObject &json) 726 | { 727 | return bodyWithJson(json); 728 | } 729 | 730 | HttpRequest &HttpRequest::bodyWithJson(const QJsonObject &json) 731 | { 732 | const QByteArray &value = QJsonDocument(json).toJson(); 733 | m_body = qMakePair(Raw_Json, value); 734 | m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); 735 | 736 | return *this; 737 | } 738 | 739 | HttpRequest &HttpRequest::body(const QByteArray &raw) 740 | { 741 | return bodyWithRaw(raw); 742 | } 743 | 744 | HttpRequest &HttpRequest::bodyWithRaw(const QByteArray &raw) 745 | { 746 | m_body = qMakePair(Raw, raw); 747 | return *this; 748 | } 749 | 750 | HttpRequest &HttpRequest::body(QHttpMultiPart *multiPart) 751 | { 752 | return bodyWithMultiPart(multiPart); 753 | } 754 | 755 | HttpRequest &HttpRequest::bodyWithMultiPart(QHttpMultiPart *multiPart) 756 | { 757 | m_body = qMakePair(MultiPart, QVariant::fromValue(multiPart)); 758 | return *this; 759 | } 760 | 761 | HttpRequest &HttpRequest::download() 762 | { 763 | return download(""); 764 | } 765 | 766 | HttpRequest &HttpRequest::download(const QString &file) 767 | { 768 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) 769 | this->attribute(QNetworkRequest::RedirectPolicyAttribute, true); 770 | #else 771 | this->attribute(QNetworkRequest::FollowRedirectsAttribute, true); 772 | #endif 773 | 774 | m_downloader.isEnabled = true; 775 | m_downloader.fileName = file; 776 | return *this; 777 | } 778 | 779 | HttpRequest &HttpRequest::enabledBreakpointDownload(bool enabled) 780 | { 781 | m_downloader.enabledBreakpointDownload = enabled; 782 | return *this; 783 | } 784 | 785 | HttpRequest &HttpRequest::onDownloadFileProgress(const QObject *receiver, const char *method) 786 | { 787 | return onResponse(h_onDownloadFileProgess, receiver, method); 788 | } 789 | 790 | HttpRequest &HttpRequest::onDownloadFileProgress(std::function lambda) 791 | { 792 | return onResponse(h_onDownloadFileProgess, QVariant::fromValue(lambda)); 793 | } 794 | 795 | HttpRequest &HttpRequest::body(const QString &key, const QString &file) 796 | { 797 | return bodyWithFile(key, file); 798 | } 799 | 800 | HttpRequest &HttpRequest::bodyWithFile(const QString &key, const QString &filePath) 801 | { 802 | QMap map; 803 | map[key] = filePath; 804 | 805 | return bodyWithFile(map); 806 | } 807 | 808 | HttpRequest &HttpRequest::bodyWithFile(const QMap &fileMap) 809 | { 810 | auto &body = m_body; 811 | auto map = body.second.value>(); 812 | for (auto each : fileMap.toStdMap()) { 813 | map[each.first] = each.second; 814 | } 815 | 816 | body.first = BodyType::FileMap; 817 | body.second = QVariant::fromValue(map); 818 | return *this; 819 | } 820 | 821 | HttpRequest &HttpRequest::ignoreSslErrors(const QList &errors) 822 | { 823 | m_ignoreSslErrors = errors; 824 | return *this; 825 | } 826 | 827 | HttpRequest &HttpRequest::sslConfiguration(const QSslConfiguration &config) 828 | { 829 | m_request.setSslConfiguration(config); 830 | return *this; 831 | } 832 | 833 | HttpRequest &HttpRequest::priority(QNetworkRequest::Priority priority) 834 | { 835 | m_request.setPriority(priority); 836 | return *this; 837 | } 838 | 839 | HttpRequest &HttpRequest::maximumRedirectsAllowed(int maxRedirectsAllowed) 840 | { 841 | m_request.setMaximumRedirectsAllowed(maxRedirectsAllowed); 842 | return *this; 843 | } 844 | 845 | HttpRequest &HttpRequest::originatingObject(QObject *object) 846 | { 847 | m_request.setOriginatingObject(object); 848 | return *this; 849 | } 850 | 851 | HttpRequest &HttpRequest::readBufferSize(qint64 size) 852 | { 853 | m_readBufferSize = size; 854 | return *this; 855 | } 856 | 857 | HttpRequest &HttpRequest::autoAuthenticationRequired(const QAuthenticator &authenticator) 858 | { 859 | m_authenticator = authenticator; 860 | return *this; 861 | } 862 | 863 | HttpRequest &HttpRequest::autoAuthenticationRequired(const QString &user, const QString &password) 864 | { 865 | QAuthenticator a; 866 | a.setUser(user); 867 | a.setPassword(password); 868 | 869 | return autoAuthenticationRequired(a); 870 | } 871 | 872 | HttpRequest &HttpRequest::authenticationRequiredCount(int count) 873 | { 874 | m_authenticationRequiredCount = count; 875 | return *this; 876 | } 877 | 878 | HttpRequest &HttpRequest::timeout(const int &second) { return timeoutMs(second * 1000); } 879 | HttpRequest &HttpRequest::timeoutMs(const int &msec) { m_timeoutMs = msec; return *this; } 880 | 881 | HttpRequest &HttpRequest::retry(int count) { m_retryCount = count; return *this; } 882 | 883 | HttpRequest &HttpRequest::repeat(int count) { m_repeatCount = count; return *this; } 884 | 885 | HttpRequest &HttpRequest::block(bool isBlock) { m_isBlock = isBlock; return *this; } 886 | HttpRequest &HttpRequest::sync(bool isSync) { return block(isSync); } 887 | 888 | HttpRequest &HttpRequest::logLevel(HttpRequest::LogLevel level) { m_logLevel = level; return *this; } 889 | 890 | HttpRequest &HttpRequest::onFinished(const QObject *receiver, const char *method) { return onResponse(h_onFinished, receiver, method); } 891 | HttpRequest &HttpRequest::onFinished(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 892 | HttpRequest &HttpRequest::onFinished(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 893 | HttpRequest &HttpRequest::onFinished(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 894 | HttpRequest &HttpRequest::onFinished(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 895 | HttpRequest &HttpRequest::onFinished(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 896 | 897 | HttpRequest &HttpRequest::onSuccess(const QObject *receiver, const char *method) { return onFinished(receiver, method); } 898 | HttpRequest &HttpRequest::onSuccess(std::function lambda) { return onFinished(lambda); } 899 | HttpRequest &HttpRequest::onSuccess(std::function lambda) { return onFinished(lambda); } 900 | HttpRequest &HttpRequest::onSuccess(std::function lambda) { return onFinished(lambda); } 901 | HttpRequest &HttpRequest::onSuccess(std::function lambda) { return onFinished(lambda); } 902 | HttpRequest &HttpRequest::onSuccess(std::function lambda) { return onFinished(lambda); } 903 | 904 | HttpRequest &HttpRequest::onFailed(const QObject *receiver, const char *method) { return onError(receiver, method); } 905 | HttpRequest &HttpRequest::onFailed(std::function lambda) { return onError(lambda); } 906 | HttpRequest &HttpRequest::onFailed(std::function lambda) { return onError(lambda); } 907 | HttpRequest &HttpRequest::onFailed(std::function lambda) { return onError(lambda); } 908 | HttpRequest &HttpRequest::onFailed(std::function lambda) { return onError(lambda); } 909 | 910 | HttpRequest &HttpRequest::onError(const QObject *receiver, const char *method) { return onResponse(h_onError, receiver, method); } 911 | HttpRequest &HttpRequest::onError(std::function lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); } 912 | HttpRequest &HttpRequest::onError(std::function lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); } 913 | HttpRequest &HttpRequest::onError(std::function lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); } 914 | HttpRequest &HttpRequest::onError(std::function lambda) { return onResponse(h_onError, QVariant::fromValue(lambda)); } 915 | 916 | HttpRequest &HttpRequest::onReadyRead(const QObject *receiver, const char *method) { return onResponse(h_onReadyRead, receiver, method); } 917 | HttpRequest &HttpRequest::onReadyRead(std::function lambda) { return onResponse(h_onReadyRead, QVariant::fromValue(lambda)); } 918 | 919 | HttpRequest &HttpRequest::onHead(const QObject *receiver, const char *method) { return onResponse(h_onHead, receiver, method); } 920 | HttpRequest &HttpRequest::onHead(std::function)> lambda) { return onResponse(h_onHead, QVariant::fromValue(lambda)); } 921 | HttpRequest &HttpRequest::onHead(std::function)> lambda) { return onResponse(h_onHead, QVariant::fromValue(lambda)); } 922 | 923 | HttpRequest &HttpRequest::onDownloadProgress(const QObject *receiver, const char *method) { return onResponse(h_onDownloadProgress, receiver, method); } 924 | HttpRequest &HttpRequest::onDownloadProgress(std::function lambda) { return onResponse(h_onDownloadProgress, QVariant::fromValue(lambda)); } 925 | 926 | HttpRequest &HttpRequest::onDownloadFileNameChanged(const QObject *receiver, const char *method) { return onResponse(h_onDownloadFileNameChanged, receiver, method); } 927 | HttpRequest &HttpRequest::onDownloadFileNameChanged(std::function lambda) { return onResponse(h_onDownloadFileNameChanged, QVariant::fromValue(lambda)); } 928 | 929 | HttpRequest &HttpRequest::onDownloadFileSuccess(const QObject *receiver, const char *method) { return onResponse(h_onDownloadFileSuccess, receiver, method); } 930 | HttpRequest &HttpRequest::onDownloadFileSuccess(std::function lambda) { return onResponse(h_onDownloadFileSuccess, QVariant::fromValue(lambda)); } 931 | HttpRequest &HttpRequest::onDownloadFileSuccess(std::function lambda) { return onResponse(h_onDownloadFileSuccess, QVariant::fromValue(lambda)); } 932 | 933 | HttpRequest &HttpRequest::onDownloadFileFailed(const QObject *receiver, const char *method) { return onResponse(h_onDownloadFileFailed, receiver, method); } 934 | HttpRequest &HttpRequest::onDownloadFileFailed(std::function lambda) { return onResponse(h_onDownloadFileFailed, QVariant::fromValue(lambda)); } 935 | HttpRequest &HttpRequest::onDownloadFileFailed(std::function lambda) { return onResponse(h_onDownloadFileFailed, QVariant::fromValue(lambda)); } 936 | 937 | HttpRequest &HttpRequest::onUploadProgress(const QObject *receiver, const char *method) { return onResponse(h_onUploadProgress, receiver, method); } 938 | HttpRequest &HttpRequest::onUploadProgress(std::function lambda) { return onResponse(h_onUploadProgress, QVariant::fromValue(lambda)); } 939 | 940 | HttpRequest &HttpRequest::onTimeout(const QObject *receiver, const char *method) { return onResponse(h_onTimeout, receiver, method); } 941 | HttpRequest &HttpRequest::onTimeout(std::function lambda) { return onResponse(h_onTimeout, QVariant::fromValue(lambda)); } 942 | HttpRequest &HttpRequest::onTimeout(std::function lambda) { return onResponse(h_onTimeout, QVariant::fromValue(lambda)); } 943 | 944 | HttpRequest &HttpRequest::onRetried(const QObject *receiver, const char *method) { return onResponse(h_onRetried, receiver, method); } 945 | HttpRequest &HttpRequest::onRetried(std::function lambda) { return onResponse(h_onRetried, QVariant::fromValue(lambda)); } 946 | 947 | HttpRequest &HttpRequest::onRepeated(const QObject *receiver, const char *method) { return onResponse(h_onRepeated, receiver, method); } 948 | HttpRequest &HttpRequest::onRepeated(std::function lambda) { return onResponse(h_onRepeated, QVariant::fromValue(lambda)); } 949 | 950 | HttpRequest &HttpRequest::onAuthenticationRequired(const QObject *receiver, const char *method) { return onResponse(h_onAuthenticationRequired, receiver, method); } 951 | HttpRequest &HttpRequest::onAuthenticationRequired(std::function lambda) { return onResponse(h_onAuthenticationRequired, QVariant::fromValue(lambda)); } 952 | 953 | HttpRequest &HttpRequest::onAuthenticationRequireFailed(const QObject *receiver, const char *method) { return onResponse(h_onAuthenticationRequireFailed, receiver, method); } 954 | HttpRequest &HttpRequest::onAuthenticationRequireFailed(std::function lambda) { return onResponse(h_onAuthenticationRequireFailed, QVariant::fromValue(lambda)); } 955 | HttpRequest &HttpRequest::onAuthenticationRequireFailed(std::function lambda) { return onResponse(h_onAuthenticationRequireFailed, QVariant::fromValue(lambda)); } 956 | 957 | HttpRequest &HttpRequest::onResponse(const QObject *receiver, const char *method) { return onResponse(h_onFinished, receiver, method); } 958 | HttpRequest &HttpRequest::onResponse(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 959 | HttpRequest &HttpRequest::onResponse(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 960 | HttpRequest &HttpRequest::onResponse(std::function lambda) { return onResponse(h_onFinished, QVariant::fromValue(lambda)); } 961 | HttpRequest &HttpRequest::onResponse(HandleType type, QVariant lambda) { return onResponse(type, lambda.typeName(), lambda); } 962 | HttpRequest &HttpRequest::onResponse(HandleType type, const QObject *receiver, const char *method) { return onResponse(type, method, QVariant::fromValue((QObject *)receiver)); } 963 | HttpRequest &HttpRequest::onResponse(HandleType type, QString key, QVariant value) 964 | { 965 | if (!m_handleMap.contains(type)) { 966 | QList > handleList; 967 | m_handleMap.insert(type, handleList); 968 | } 969 | 970 | auto handleList = m_handleMap[type]; 971 | handleList.append({key, value}); 972 | 973 | m_handleMap.insert(type, handleList); 974 | return *this; 975 | } 976 | 977 | QString HttpRequest::toString() 978 | { 979 | QString str = \ 980 | "General: \n" \ 981 | " Request URL: %{url} \n" \ 982 | " Request Method: %{method} \n" \ 983 | "Request Headers: \n" \ 984 | "%{requestHeaders} \n" \ 985 | "Request Body: \n" \ 986 | "%{requestBody}"; 987 | 988 | str.replace("%{url}", m_request.url().toString()); 989 | str.replace("%{method}", networkOperation2String(m_op)); 990 | str.replace("%{requestHeaders}", lineIndent(networkHeader2String(m_request), " ")); 991 | str.replace("%{requestBody}", lineIndent(networkBody2String(m_body), " ")); 992 | 993 | return str; 994 | } 995 | 996 | HttpResponse *HttpRequest::exec(const HttpRequest &_httpRequest, HttpResponse *httpResponse) 997 | { 998 | HttpRequest httpRequest = _httpRequest; 999 | 1000 | QByteArray op = networkOperation2String(httpRequest.m_op).toUtf8(); 1001 | if (op.isEmpty()) { 1002 | QString str = QString("Url: [%1]; Method: [%2] not support!").arg(httpRequest.m_request.url().toString()).arg(QString(op)); 1003 | printError(httpRequest.m_logLevel, str); 1004 | return nullptr; 1005 | } 1006 | 1007 | using BodyType = HttpRequest::BodyType; 1008 | BodyType bodyType = httpRequest.m_body.first; 1009 | QVariant body = httpRequest.m_body.second; 1010 | QNetworkRequest request = httpRequest.m_request; 1011 | HttpClient *httpClient = httpRequest.m_httpClient; 1012 | 1013 | if (bodyType == BodyType::MultiPart) { 1014 | QHttpMultiPart *multiPart = body.value(); 1015 | QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data()); 1016 | 1017 | request.setHeader(QNetworkRequest::ContentTypeHeader, contentType); 1018 | 1019 | httpRequest.m_reply = httpRequest.m_httpClient->sendCustomRequest(request, op, multiPart); 1020 | multiPart->setParent(httpRequest.m_reply); 1021 | 1022 | } 1023 | else if (bodyType == BodyType::FileMap) { 1024 | QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); 1025 | QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data()); 1026 | request.setHeader(QNetworkRequest::ContentTypeHeader, contentType); 1027 | 1028 | const auto &fileMap = body.value>(); 1029 | for (const auto &each : fileMap.toStdMap()) { 1030 | const QString &key = each.first; 1031 | const QString &filePath = each.second; 1032 | 1033 | QFile *file = new QFile(filePath); 1034 | file->open(QIODevice::ReadOnly); 1035 | file->setParent(multiPart); 1036 | 1037 | // todo 1038 | // part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); 1039 | 1040 | // note: "form-data; name=\"%1\";filename=\"%2\"" != "form-data; name=\"%1\";filename=\"%2\";" 1041 | QString dispositionHeader = QString("form-data; name=\"%1\";filename=\"%2\"") 1042 | .arg(key) 1043 | .arg(QFileInfo(filePath).fileName()); 1044 | QHttpPart part; 1045 | part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader); 1046 | part.setBodyDevice(file); 1047 | 1048 | multiPart->append(part); 1049 | } 1050 | 1051 | httpRequest.m_reply = httpClient->sendCustomRequest(request, op, multiPart); 1052 | if (httpRequest.m_reply) 1053 | multiPart->setParent(httpRequest.m_reply); 1054 | else 1055 | delete multiPart; 1056 | 1057 | } 1058 | else if (bodyType == BodyType::FormData) { 1059 | QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); 1060 | QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart->boundary().data()); 1061 | request.setHeader(QNetworkRequest::ContentTypeHeader, contentType); 1062 | 1063 | const auto &formDataMap = body.value>(); 1064 | for (const auto &each : formDataMap.toStdMap()) { 1065 | const QString &key = each.first; 1066 | const QString &value = each.second.toString(); 1067 | 1068 | QString dispositionHeader = QString("form-data; name=\"%1\"").arg(key); 1069 | 1070 | QHttpPart part; 1071 | part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader); 1072 | part.setBody(value.toUtf8()); 1073 | 1074 | multiPart->append(part); 1075 | } 1076 | 1077 | httpRequest.m_reply = httpClient->sendCustomRequest(request, op, multiPart); 1078 | 1079 | if (httpRequest.m_reply) 1080 | multiPart->setParent(httpRequest.m_reply); 1081 | else 1082 | delete multiPart; 1083 | } 1084 | else { 1085 | httpRequest.m_reply = httpClient->sendCustomRequest(request, op, body.toByteArray()); 1086 | } 1087 | 1088 | if (httpRequest.m_reply == nullptr) { 1089 | // fixme: todo onError 1090 | printError(httpRequest.m_logLevel, "Http reply invalid"); 1091 | Q_ASSERT(httpRequest.m_reply); 1092 | return nullptr; 1093 | } 1094 | 1095 | static int count = 0; 1096 | httpRequest.m_reply->setProperty("count", count++); 1097 | 1098 | // fixme 1099 | if (!httpRequest.m_ignoreSslErrors.isEmpty()) { 1100 | httpRequest.m_reply->ignoreSslErrors(httpRequest.m_ignoreSslErrors); 1101 | } 1102 | 1103 | if (httpRequest.m_readBufferSize >= 0) { 1104 | httpRequest.m_reply->setReadBufferSize(httpRequest.m_readBufferSize); 1105 | } 1106 | 1107 | printDebug(httpRequest.m_logLevel, toString()); 1108 | 1109 | if (httpResponse) { 1110 | httpResponse->setParent(httpRequest.m_reply); 1111 | httpResponse->setHttpRequest(httpRequest); 1112 | return httpResponse; 1113 | } 1114 | else { 1115 | return new HttpResponse(httpRequest, httpRequest.m_reply); 1116 | } 1117 | } 1118 | 1119 | inline QDebug operator<<(QDebug debug, const QNetworkAccessManager::Operation &op) 1120 | { 1121 | QDebugStateSaver saver(debug); 1122 | debug.nospace(); 1123 | 1124 | switch (op) { 1125 | case QNetworkAccessManager::HeadOperation: return debug << "HeadOperation"; 1126 | case QNetworkAccessManager::GetOperation: return debug << "GetOperation"; 1127 | case QNetworkAccessManager::PostOperation: return debug << "PostOperation"; 1128 | case QNetworkAccessManager::PutOperation: return debug << "PutOperation"; 1129 | case QNetworkAccessManager::DeleteOperation: return debug << "DeleteOperation"; 1130 | case QNetworkAccessManager::CustomOperation: return debug << "CustomOperation"; 1131 | default: return debug << "UnknownOperation"; 1132 | } 1133 | } 1134 | 1135 | inline QDebug operator<<(QDebug debug, const HttpRequest::HandleType &handleType) 1136 | { 1137 | QDebugStateSaver saver(debug); 1138 | debug.nospace(); 1139 | 1140 | switch (handleType) { 1141 | case HttpRequest::h_onFinished: return debug << "onFinished"; 1142 | case HttpRequest::h_onError: return debug << "onError"; 1143 | case HttpRequest::h_onDownloadProgress: return debug << "onDownloadProgress"; 1144 | case HttpRequest::h_onUploadProgress: return debug << "onUploadProgress"; 1145 | // todo: onUploadProgressSuccess and onUploadProgressFaied 1146 | case HttpRequest::h_onDownloadFileSuccess: return debug << "onDownloadFileSuccess"; 1147 | case HttpRequest::h_onDownloadFileFailed: return debug << "onDownloadFileFailed"; 1148 | case HttpRequest::h_onTimeout: return debug << "onTimeout"; 1149 | case HttpRequest::h_onReadyRead: return debug << "onReadyRead"; 1150 | case HttpRequest::h_onEncrypted: return debug << "onEncrypted"; 1151 | case HttpRequest::h_onMetaDataChanged: return debug << "onMetaChanged"; 1152 | case HttpRequest::h_onPreSharedKeyAuthenticationRequired: return debug << "onPreSharedKeyAuthenticationRequired"; 1153 | case HttpRequest::h_onRedirectAllowed: return debug << "onRedirectAllowed"; 1154 | case HttpRequest::h_onRedirected: return debug << "onRedirected"; 1155 | case HttpRequest::h_onSslErrors: return debug << "onSslErrors"; 1156 | case HttpRequest::h_onRetried: return debug << "onRetried"; 1157 | case HttpRequest::h_onRepeated: return debug << "onRepeated"; 1158 | case HttpRequest::h_onAuthenticationRequired: return debug << "onAuthenticationRequired"; 1159 | case HttpRequest::h_onAuthenticationRequireFailed: return debug << "onAuthenticationRequireFailed"; 1160 | case HttpRequest::h_onHead: return debug << "onHead"; 1161 | case HttpRequest::h_onDownloadFileProgess: return debug << "onDownloadFileProgress"; 1162 | case HttpRequest::h_onDownloadFileNameChanged: return debug << "onDownloadFileNameChanged"; 1163 | default: return debug << "Unknow"; 1164 | } 1165 | } 1166 | 1167 | static int extractCode(const char *member) 1168 | { 1169 | /* extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE */ 1170 | return (((int)(*member) - '0') & 0x3); 1171 | } 1172 | 1173 | static bool isMethod(const char *member) 1174 | { 1175 | int ret = extractCode(member); 1176 | return ret >= QMETHOD_CODE && ret <= QSIGNAL_CODE; 1177 | } 1178 | 1179 | template 1180 | bool httpResponseConnect(L sender, T senderSignal, const QString &lambdaString, const QVariant &lambda) 1181 | { 1182 | if (lambdaString == QVariant::fromValue(M()).typeName()) { 1183 | return QObject::connect(sender, senderSignal, lambda.value()); 1184 | } 1185 | else if (isMethod(qPrintable(lambdaString))) { 1186 | QString signal = QMetaMethod::fromSignal(senderSignal).methodSignature(); 1187 | signal.insert(0, "2"); 1188 | signal.replace("qlonglong", "qint64"); 1189 | 1190 | const QObject *receiver = lambda.value(); 1191 | QString method = QMetaObject::normalizedSignature(qPrintable(lambdaString)); // remove 'const', like: const QString => QString 1192 | 1193 | if (QMetaObject::checkConnectArgs(qPrintable(signal), qPrintable(method))) { 1194 | return QObject::connect(sender, qPrintable(signal), receiver, qPrintable(method)); 1195 | } 1196 | else { 1197 | return false; 1198 | } 1199 | } 1200 | else { 1201 | return false; 1202 | } 1203 | } 1204 | 1205 | #define HTTP_RESPONSE_CONNECT_X(sender, senderSignal, lambdaString, lambda, ...) \ 1206 | httpResponseConnect< std::function > ( \ 1207 | sender, \ 1208 | static_cast(&HttpResponse::senderSignal), \ 1209 | lambdaString, \ 1210 | lambda); 1211 | 1212 | HttpResponse *HttpRequest::exec() 1213 | { 1214 | if (this->m_downloader.isEnabled) { 1215 | HttpDownloader *downloader = new HttpDownloader(*this, nullptr); 1216 | HttpResponse *response = downloader->exec(); 1217 | downloader->setParent(response); 1218 | return response; 1219 | } 1220 | else { 1221 | return exec(*this); 1222 | } 1223 | } 1224 | 1225 | HttpRequest &HttpRequest::enabledRetry(bool isEnabled) 1226 | { 1227 | m_enabledRetry = isEnabled; 1228 | return *this; 1229 | } 1230 | 1231 | HttpRequest &HttpRequest::queryParam(const QString &key, const QVariant &value) 1232 | { 1233 | QUrl url(m_request.url()); 1234 | QUrlQuery urlQuery(url); 1235 | 1236 | urlQuery.addQueryItem(key, value.toString()); 1237 | url.setQuery(urlQuery); 1238 | 1239 | m_request.setUrl(url); 1240 | 1241 | return *this; 1242 | } 1243 | 1244 | HttpRequest &HttpRequest::queryParams(const QMap ¶ms) 1245 | { 1246 | QMapIterator iter(params); 1247 | while (iter.hasNext()) { 1248 | iter.next(); 1249 | queryParam(iter.key(), iter.value()); 1250 | } 1251 | 1252 | return *this; 1253 | } 1254 | 1255 | HttpRequest &HttpRequest::userAttribute(const QVariant &value) 1256 | { 1257 | m_request.setAttribute(QNetworkRequest::User, value); 1258 | return *this; 1259 | } 1260 | 1261 | HttpRequest &HttpRequest::attribute(QNetworkRequest::Attribute attribute, const QVariant &value) 1262 | { 1263 | m_request.setAttribute(attribute, value); 1264 | return *this; 1265 | } 1266 | 1267 | HttpClient *HttpClient::instance() 1268 | { 1269 | static HttpClient client; 1270 | return &client; 1271 | } 1272 | 1273 | HttpClient::HttpClient(QObject *parent) : QNetworkAccessManager(parent) 1274 | { 1275 | } 1276 | 1277 | HttpRequest HttpClient::head(const QString &url) 1278 | { 1279 | return HttpRequest(QNetworkAccessManager::HeadOperation, this).url(url); 1280 | } 1281 | 1282 | HttpRequest HttpClient::get(const QString &url) 1283 | { 1284 | return HttpRequest(QNetworkAccessManager::GetOperation, this).url(url); 1285 | } 1286 | 1287 | HttpRequest HttpClient::post(const QString &url) 1288 | { 1289 | return HttpRequest(QNetworkAccessManager::PostOperation, this).url(url); 1290 | } 1291 | 1292 | HttpRequest HttpClient::put(const QString &url) 1293 | { 1294 | return HttpRequest(QNetworkAccessManager::PutOperation, this).url(url); 1295 | } 1296 | 1297 | HttpRequest HttpClient::send(const QString &url, QNetworkAccessManager::Operation op) 1298 | { 1299 | return HttpRequest(op, this).url(url); 1300 | } 1301 | 1302 | #if (QT_VERSION < QT_VERSION_CHECK(5, 8, 0)) 1303 | QNetworkReply *HttpClient::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data) 1304 | { 1305 | QBuffer *buffer = new QBuffer; 1306 | buffer->setData(data); 1307 | buffer->open(QIODevice::ReadOnly); 1308 | QNetworkReply *reply = QNetworkAccessManager::sendCustomRequest(request, verb, buffer); 1309 | buffer->setParent(reply); 1310 | 1311 | return reply; 1312 | } 1313 | 1314 | QNetworkReply *HttpClient::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart) 1315 | { 1316 | if (verb == "PUT") { 1317 | return QNetworkAccessManager::put(request, multiPart); 1318 | } 1319 | else if (verb == "POST") { 1320 | return QNetworkAccessManager::post(request, multiPart); 1321 | } 1322 | else { 1323 | qWarning() << "not support " << verb << "multi part."; 1324 | return nullptr; 1325 | } 1326 | } 1327 | #endif 1328 | 1329 | HttpResponse::HttpResponse(const HttpRequest &httpRequest, QObject *parent) 1330 | : QObject(parent), 1331 | m_httpRequest(httpRequest), 1332 | m_retriesRemaining(httpRequest.m_retryCount) 1333 | { 1334 | this->setHttpRequest(httpRequest); 1335 | } 1336 | 1337 | HttpResponse::~HttpResponse() 1338 | { 1339 | } 1340 | 1341 | void HttpResponse::setHttpRequest(const HttpRequest &httpRequest) 1342 | { 1343 | if (httpRequest.m_timeoutMs > 0) { 1344 | new HttpResponseTimeout(this, httpRequest.m_timeoutMs); 1345 | } 1346 | 1347 | QNetworkReply *reply = httpRequest.m_reply; 1348 | if (reply) { 1349 | connect(reply, SIGNAL(finished()), this, SLOT(onFinished())); 1350 | connect(reply, SIGNAL(finished()), this, SLOT(onHandleHead())); 1351 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) 1352 | connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError))); 1353 | #else 1354 | connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError))); 1355 | #endif 1356 | 1357 | connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64))); 1358 | connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(onUploadProgress(qint64, qint64))); 1359 | 1360 | connect(reply, SIGNAL(readyRead()), this, SLOT(onReadOnceReplyHeader())); 1361 | connect(reply, SIGNAL(readyRead()), this, SLOT(onHandleHead())); 1362 | connect(reply, SIGNAL(readyRead()), this, SLOT(onReadyRead())); 1363 | 1364 | connect(reply, SIGNAL(encrypted()), this, SLOT(onEncrypted())); 1365 | connect(reply, SIGNAL(metaDataChanged()), this, SLOT(onMetaDataChanged())); 1366 | 1367 | connect(reply, SIGNAL(redirected(QUrl)), this, SLOT(onRedirected(QUrl))); 1368 | connect(reply, SIGNAL(sslErrors(QList)), this, SLOT(onSslErrors(QList))); 1369 | 1370 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) 1371 | connect(reply, SIGNAL(redirectAllowed()), this, SLOT(onRedirectAllowed())); 1372 | #endif 1373 | 1374 | connect(reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), this, SLOT(onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))); 1375 | 1376 | connect(reply->manager(), SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(onAuthenticationRequired(QNetworkReply*,QAuthenticator*))); 1377 | 1378 | // fixme: Too cumbersome 1379 | for (auto each : httpRequest.m_handleMap.toStdMap()) { 1380 | const HttpRequest::HandleType &key = each.first; 1381 | const QList> &value = each.second; 1382 | 1383 | for (auto iter : value) { 1384 | const QVariant &lambda = iter.second; 1385 | const QString &lambdaString = iter.first; 1386 | int ret = 0; 1387 | 1388 | if (key == HttpRequest::h_onFinished) { 1389 | ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, void); 1390 | ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QByteArray); 1391 | ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QString); 1392 | ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QVariantMap); 1393 | ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QJsonObject); 1394 | ret += HTTP_RESPONSE_CONNECT_X(this, finished, lambdaString, lambda, QNetworkReply*); 1395 | } 1396 | else if (key == HttpRequest::h_onDownloadProgress) { 1397 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadProgress, lambdaString, lambda, qint64, qint64); 1398 | } 1399 | else if (key == HttpRequest::h_onUploadProgress) { 1400 | ret += HTTP_RESPONSE_CONNECT_X(this, uploadProgress, lambdaString, lambda, qint64, qint64); 1401 | } 1402 | else if (key == HttpRequest::h_onError) { 1403 | ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, void); 1404 | ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QByteArray); 1405 | ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QString); 1406 | ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QNetworkReply*); 1407 | ret += HTTP_RESPONSE_CONNECT_X(this, error, lambdaString, lambda, QNetworkReply::NetworkError); 1408 | } 1409 | else if (key == HttpRequest::h_onTimeout) { 1410 | ret += HTTP_RESPONSE_CONNECT_X(this, timeout, lambdaString, lambda, QNetworkReply*); 1411 | ret += HTTP_RESPONSE_CONNECT_X(this, timeout, lambdaString, lambda, void); 1412 | } 1413 | else if (key == HttpRequest::h_onReadyRead) { 1414 | ret += HTTP_RESPONSE_CONNECT_X(this, readyRead, lambdaString, lambda, QNetworkReply*); 1415 | } 1416 | else if (key == HttpRequest::h_onDownloadFileSuccess) { 1417 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileFinished, lambdaString, lambda, void); 1418 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileFinished, lambdaString, lambda, QString); 1419 | } 1420 | else if (key == HttpRequest::h_onDownloadFileFailed) { 1421 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileError, lambdaString, lambda, void); 1422 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileError, lambdaString, lambda, QString); 1423 | } 1424 | else if (key == HttpRequest::h_onEncrypted) { 1425 | ret += HTTP_RESPONSE_CONNECT_X(this, encrypted, lambdaString, lambda, void); 1426 | } 1427 | else if (key == HttpRequest::h_onMetaDataChanged) { 1428 | ret += HTTP_RESPONSE_CONNECT_X(this, metaDataChanged, lambdaString, lambda, void); 1429 | } 1430 | else if (key == HttpRequest::h_onPreSharedKeyAuthenticationRequired) { 1431 | ret += HTTP_RESPONSE_CONNECT_X(this, preSharedKeyAuthenticationRequired, lambdaString, lambda, QSslPreSharedKeyAuthenticator*); 1432 | } 1433 | else if (key == HttpRequest::h_onRedirectAllowed) { 1434 | ret += HTTP_RESPONSE_CONNECT_X(this, redirectAllowed, lambdaString, lambda, void); 1435 | } 1436 | else if (key == HttpRequest::h_onRedirected) { 1437 | ret += HTTP_RESPONSE_CONNECT_X(this, redirected, lambdaString, lambda, QUrl); 1438 | } 1439 | else if (key == HttpRequest::h_onSslErrors) { 1440 | ret += HTTP_RESPONSE_CONNECT_X(this, sslErrors, lambdaString, lambda, QList); 1441 | } 1442 | else if (key == HttpRequest::h_onRetried) { 1443 | ret += HTTP_RESPONSE_CONNECT_X(this, retried, lambdaString, lambda, void); 1444 | } 1445 | else if (key == HttpRequest::h_onRepeated) { 1446 | ret += HTTP_RESPONSE_CONNECT_X(this, repeated, lambdaString, lambda, void); 1447 | } 1448 | else if (key == HttpRequest::h_onAuthenticationRequired) { 1449 | ret += HTTP_RESPONSE_CONNECT_X(this, authenticationRequired, lambdaString, lambda, QAuthenticator*); 1450 | } 1451 | else if (key == HttpRequest::h_onAuthenticationRequireFailed) { 1452 | ret += HTTP_RESPONSE_CONNECT_X(this, authenticationRequireFailed, lambdaString, lambda, void); 1453 | ret += HTTP_RESPONSE_CONNECT_X(this, authenticationRequireFailed, lambdaString, lambda, QNetworkReply*); 1454 | } 1455 | else if (key == HttpRequest::h_onHead) { 1456 | ret += HTTP_RESPONSE_CONNECT_X(this, head, lambdaString, lambda, QList); 1457 | ret += HTTP_RESPONSE_CONNECT_X(this, head, lambdaString, lambda, QMap); 1458 | } 1459 | else if (key == HttpRequest::h_onDownloadFileProgess) { 1460 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileProgress, lambdaString, lambda, qint64, qint64); 1461 | } 1462 | else if (key == HttpRequest::h_onDownloadFileNameChanged) { 1463 | ret += HTTP_RESPONSE_CONNECT_X(this, downloadFileNameChanged, lambdaString, lambda, QString); 1464 | } 1465 | else { 1466 | printWarn(httpRequest.m_logLevel, QString("%1 unsupported").arg(key)); 1467 | } 1468 | 1469 | if (ret == 0) { 1470 | QString method = lambdaString; 1471 | if (isMethod(qPrintable(method))) 1472 | method.remove(0, 1); 1473 | 1474 | printWarn(httpRequest.m_logLevel, QString("%1 method[%2] is invalid").arg(key).arg(method)); 1475 | } 1476 | } 1477 | } 1478 | } 1479 | 1480 | const HttpRequest &oldRequest = m_httpRequest; 1481 | m_httpRequest = httpRequest; 1482 | 1483 | if (oldRequest.m_reply != httpRequest.m_reply) { 1484 | emit replyChanged(httpRequest.m_reply); 1485 | } 1486 | 1487 | if (reply && httpRequest.m_isBlock) { 1488 | new HttpBlocker(reply); 1489 | } 1490 | } 1491 | 1492 | QString HttpResponse::toString() const 1493 | { 1494 | QString str = \ 1495 | "General: \n" \ 1496 | " Request URL: %{url} \n" \ 1497 | " Request Method: %{method} \n" \ 1498 | " Request Status: %{status}(%{statusString}) \n" \ 1499 | "Request Headers: \n" \ 1500 | "%{requestHeaders} \n" \ 1501 | "Response Headers: \n" \ 1502 | "%{responseHeaders} \n" \ 1503 | "Request Body: \n" \ 1504 | "%{requestBody}"; 1505 | 1506 | QNetworkReply *reply = this->m_httpRequest.m_reply; 1507 | str.replace("%{url}", this->m_httpRequest.m_request.url().toString()); 1508 | str.replace("%{method}", networkOperation2String(m_httpRequest.m_op)); 1509 | str.replace("%{status}", QString::number(reply->error())); 1510 | str.replace("%{statusString}", reply->errorString()); 1511 | str.replace("%{requestHeaders}", lineIndent(networkHeader2String(reply->request()), " ")); 1512 | str.replace("%{responseHeaders}", lineIndent(networkReplyHeader2String(reply), " ")); 1513 | str.replace("%{requestBody}", lineIndent(networkBody2String(m_httpRequest.m_body), " ")); 1514 | return str; 1515 | } 1516 | 1517 | void HttpResponse::onFinished() 1518 | { 1519 | QNetworkReply *reply = m_httpRequest.m_reply; 1520 | if (reply->error() != QNetworkReply::NoError) { 1521 | return; 1522 | } 1523 | 1524 | for (QObject *o : reply->children()) { 1525 | HttpResponse *response = qobject_cast(o); 1526 | if (response) { 1527 | printDebug(m_httpRequest.m_logLevel, response->toString()); 1528 | } 1529 | } 1530 | 1531 | if (m_httpRequest.m_enabledRetry) { 1532 | emit retried(); 1533 | } 1534 | 1535 | if (m_downloadFile.isOpen()) { 1536 | emit downloadFileFinished(); 1537 | emit downloadFileFinished(m_downloadFile.fileName()); 1538 | 1539 | m_downloadFile.close(); 1540 | } 1541 | 1542 | bool isAutoDelete = true; 1543 | if (this->receivers(SIGNAL(finished(QNetworkReply*))) > 0) { 1544 | emit finished(reply); 1545 | isAutoDelete = false; 1546 | } 1547 | 1548 | if (this->receivers(SIGNAL(finished())) > 0 || 1549 | this->receivers(SIGNAL(finished(QByteArray))) > 0 || 1550 | this->receivers(SIGNAL(finished(QString))) > 0 || 1551 | this->receivers(SIGNAL(finished(QVariantMap))) > 0 || 1552 | this->receivers(SIGNAL(finished(QJsonObject))) > 0 1553 | ) 1554 | { 1555 | QByteArray result = reply->readAll(); 1556 | emit finished(); 1557 | 1558 | emit finished(result); 1559 | 1560 | emit finished(QString(result)); 1561 | 1562 | QJsonObject json = QJsonDocument::fromJson(result).object(); 1563 | emit finished(json); 1564 | 1565 | QVariantMap resultMap = json.toVariantMap(); 1566 | emit finished(resultMap); 1567 | } 1568 | 1569 | if (--m_httpRequest.m_repeatCount > 0) { 1570 | HttpRequest httpRequest = m_httpRequest; 1571 | httpRequest.repeat(m_httpRequest.m_repeatCount) 1572 | .exec(); 1573 | } 1574 | else { 1575 | emit repeated(); 1576 | } 1577 | 1578 | if (isAutoDelete) { 1579 | reply->deleteLater(); 1580 | } 1581 | } 1582 | 1583 | void HttpResponse::onError(QNetworkReply::NetworkError error) 1584 | { 1585 | QNetworkReply *reply = m_httpRequest.m_reply; 1586 | 1587 | printInfo(m_httpRequest.m_logLevel, QString("%1 error: %2").arg(reply->url().toString()).arg(error)); 1588 | 1589 | if ( m_retriesRemaining-- > 0) { 1590 | HttpRequest httpRequest = m_httpRequest; 1591 | httpRequest.retry(m_retriesRemaining) 1592 | .enabledRetry(true) 1593 | .exec(); 1594 | reply->deleteLater(); 1595 | return; 1596 | } 1597 | 1598 | if (m_httpRequest.m_enabledRetry) { 1599 | emit retried(); 1600 | } 1601 | 1602 | const QMetaObject &metaObject = QNetworkReply::staticMetaObject; 1603 | QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("NetworkError")); 1604 | QString errorString = reply->errorString().isEmpty() ? metaEnum.valueToKey(error) : reply->errorString(); 1605 | 1606 | if (m_httpRequest.m_downloader.isEnabled) { 1607 | QString error = QString("Url: %1 file: %2 error: %3") 1608 | .arg(m_httpRequest.m_request.url().toString()) // fixme 1609 | .arg(m_downloadFile.fileName()) 1610 | .arg(errorString); 1611 | 1612 | emit downloadFileError(); 1613 | emit downloadFileError(error); 1614 | 1615 | m_downloadFile.close(); 1616 | } 1617 | 1618 | bool isAutoDelete = true; 1619 | if (this->receivers(SIGNAL(error(QNetworkReply*))) > 0) { 1620 | emit this->error(reply); 1621 | isAutoDelete = false; 1622 | } 1623 | 1624 | emit this->error(); 1625 | emit this->error(error); 1626 | emit this->error(errorString); 1627 | emit this->error(errorString.toLocal8Bit()); 1628 | 1629 | if (--m_httpRequest.m_repeatCount > 0) { 1630 | HttpRequest httpRequest = m_httpRequest; 1631 | httpRequest.repeat(m_httpRequest.m_repeatCount) 1632 | .exec(); 1633 | } 1634 | else { 1635 | emit repeated(); 1636 | } 1637 | 1638 | if (isAutoDelete) { 1639 | reply->deleteLater(); 1640 | } 1641 | } 1642 | 1643 | void HttpResponse::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) 1644 | { 1645 | emit this->downloadProgress(bytesReceived, bytesTotal); 1646 | } 1647 | 1648 | void HttpResponse::onUploadProgress(qint64 bytesSent, qint64 bytesTotal) 1649 | { 1650 | emit this->uploadProgress(bytesSent, bytesTotal); 1651 | } 1652 | 1653 | void HttpResponse::onTimeout() 1654 | { 1655 | QNetworkReply *reply = m_httpRequest.m_reply; 1656 | if (reply && reply->isRunning()) { 1657 | reply->abort(); 1658 | 1659 | bool isAutoDelete = true; 1660 | if (this->receivers(SIGNAL(timeout(QNetworkReply*))) > 0) { 1661 | emit this->timeout(reply); 1662 | isAutoDelete = false; 1663 | } 1664 | 1665 | if (this->receivers(SIGNAL(timeout())) > 0) { 1666 | emit this->timeout(); 1667 | } 1668 | 1669 | if (isAutoDelete) { 1670 | reply->deleteLater(); 1671 | } 1672 | } 1673 | } 1674 | 1675 | void HttpResponse::onReadyRead() 1676 | { 1677 | QNetworkReply *reply = m_httpRequest.m_reply; 1678 | if (m_httpRequest.m_downloader.isEnabled) { 1679 | if (m_downloadFile.isOpen()){ 1680 | int size = m_downloadFile.write(reply->readAll()); 1681 | if (size == -1) { 1682 | QString error = QString("Url: %1 %2 Write failed!") 1683 | .arg(m_httpRequest.m_request.url().toString()) 1684 | .arg(m_downloadFile.fileName()); 1685 | emit downloadFileError(); 1686 | emit downloadFileError(error); 1687 | } 1688 | else { 1689 | m_httpRequest.m_downloader.currentSize += size; 1690 | emit downloadFileProgress(m_httpRequest.m_downloader.currentSize, m_httpRequest.m_downloader.totalSize); 1691 | } 1692 | } 1693 | else { 1694 | // do nothing 1695 | } 1696 | } 1697 | else { 1698 | // do nothing 1699 | } 1700 | 1701 | emit readyRead(reply); 1702 | } 1703 | 1704 | void HttpResponse::onReadOnceReplyHeader() 1705 | { 1706 | if (! m_httpRequest.m_downloader.isEnabled) 1707 | return; 1708 | 1709 | QNetworkReply *reply = m_httpRequest.m_reply; 1710 | disconnect(reply, SIGNAL(readyRead()), this, SLOT(onReadOnceReplyHeader())); 1711 | 1712 | QString fileName = m_httpRequest.m_downloader.fileName; 1713 | m_downloadFile.setFileName(fileName); 1714 | 1715 | QIODevice::OpenMode mode = QIODevice::WriteOnly; 1716 | if (m_httpRequest.m_downloader.isSupportBreakpointDownload && 1717 | m_httpRequest.m_downloader.enabledBreakpointDownload && 1718 | QFile::exists(fileName)) 1719 | { 1720 | mode = QIODevice::Append; 1721 | } 1722 | 1723 | if (!m_downloadFile.open(mode)) { 1724 | QString error = QString("Url: %1 %2 Non-Writable") 1725 | .arg(m_httpRequest.m_request.url().toString()) 1726 | .arg(m_downloadFile.fileName()); 1727 | emit downloadFileError(); 1728 | emit downloadFileError(error); 1729 | } 1730 | else { 1731 | // todo startDownload 1732 | } 1733 | } 1734 | 1735 | void HttpResponse::onEncrypted() 1736 | { 1737 | emit encrypted(); 1738 | } 1739 | 1740 | void HttpResponse::onMetaDataChanged() 1741 | { 1742 | emit metaDataChanged(); 1743 | } 1744 | 1745 | void HttpResponse::onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator) 1746 | { 1747 | emit preSharedKeyAuthenticationRequired(authenticator); 1748 | } 1749 | 1750 | void HttpResponse::onRedirectAllowed() 1751 | { 1752 | emit redirectAllowed(); 1753 | } 1754 | 1755 | void HttpResponse::onRedirected(const QUrl &url) 1756 | { 1757 | emit redirected(url); 1758 | } 1759 | 1760 | void HttpResponse::onSslErrors(const QList &errors) 1761 | { 1762 | emit sslErrors(errors); 1763 | } 1764 | 1765 | void HttpResponse::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) 1766 | { 1767 | if (this->reply() != reply) { 1768 | return; 1769 | } 1770 | 1771 | m_authenticationCount++; 1772 | 1773 | bool isAuthenticationSuccessed = (m_authenticationCount >= 2); 1774 | if (isAuthenticationSuccessed) { 1775 | emit authenticationRequireFailed(); 1776 | emit authenticationRequireFailed(this->reply()); 1777 | } 1778 | 1779 | if (m_httpRequest.m_authenticationRequiredCount >= 0 && 1780 | m_authenticationCount > m_httpRequest.m_authenticationRequiredCount) 1781 | { 1782 | return; 1783 | } 1784 | 1785 | if (m_httpRequest.m_authenticator.isNull()) { 1786 | emit authenticationRequired(authenticator); 1787 | } 1788 | else { 1789 | authenticator->setUser(m_httpRequest.m_authenticator.user()); 1790 | authenticator->setPassword(m_httpRequest.m_authenticator.password()); 1791 | // todo setOption.... 1792 | } 1793 | } 1794 | 1795 | void HttpResponse::onHandleHead() 1796 | { 1797 | if (m_isHandleHead) { 1798 | return; 1799 | } 1800 | 1801 | m_isHandleHead = true; 1802 | 1803 | QNetworkReply *reply = m_httpRequest.m_reply; 1804 | if (this->receivers(SIGNAL(head(QList))) || 1805 | this->receivers(SIGNAL(head(QMap))) 1806 | ) 1807 | { 1808 | emit head(reply->rawHeaderPairs()); 1809 | QMap map; 1810 | foreach (auto each, reply->rawHeaderPairs()) { 1811 | map[each.first] = each.second; 1812 | } 1813 | 1814 | emit head(map); 1815 | } 1816 | } 1817 | 1818 | inline QString lineIndent(const QString &source, const QString &indentString) 1819 | { 1820 | QRegularExpression rx("^(.*)"); 1821 | QRegularExpression::PatternOptions patternOptions; 1822 | patternOptions |= QRegularExpression::MultilineOption; 1823 | rx.setPatternOptions(patternOptions); 1824 | return QString(source).replace(rx, indentString + "\\1"); 1825 | } 1826 | 1827 | inline QString networkHeader2String(const QNetworkRequest &request) 1828 | { 1829 | QString headerString; 1830 | for (const QByteArray &each : request.rawHeaderList()) { 1831 | QByteArray value = request.rawHeader(each); 1832 | headerString += QString("%1: %2\n").arg(QString(each)).arg(QString(value)); 1833 | } 1834 | 1835 | if (headerString.isEmpty()) { 1836 | return "null"; 1837 | } 1838 | 1839 | if (headerString.at(headerString.count()-1) == '\n') { 1840 | headerString.chop(1); 1841 | } 1842 | 1843 | return headerString; 1844 | } 1845 | 1846 | inline QString networkReplyHeader2String(const QNetworkReply *reply) 1847 | { 1848 | QString headerString; 1849 | for (const QByteArray &each : reply->rawHeaderList()) { 1850 | QByteArray value = reply->rawHeader(each); 1851 | headerString += QString("%1: %2\n").arg(QString(each)).arg(QString(value)); 1852 | } 1853 | 1854 | if (headerString.isEmpty()) { 1855 | return "null"; 1856 | } 1857 | 1858 | if (headerString.at(headerString.count()-1) == '\n') { 1859 | headerString.chop(1); 1860 | } 1861 | 1862 | return headerString; 1863 | } 1864 | 1865 | inline QString networkBodyType2String(HttpRequest::BodyType t) 1866 | { 1867 | if (t == HttpRequest::MultiPart) { 1868 | return "MultiPart"; 1869 | } 1870 | else if (t == HttpRequest::FileMap) { 1871 | return "FileMap"; 1872 | } 1873 | else if (t == HttpRequest::FormData) { 1874 | return "FormData"; 1875 | } 1876 | else if (t == HttpRequest::Raw) { 1877 | return "Raw"; 1878 | } 1879 | else if (t == HttpRequest::Raw_Json) { 1880 | return "RawJson"; 1881 | } 1882 | else if (t == HttpRequest::X_Www_Form_Urlencoded) { 1883 | return "x_www_form_urlencoded"; 1884 | } 1885 | else { 1886 | return "None"; 1887 | } 1888 | } 1889 | 1890 | inline QString networkBody2String(const QPair &body) 1891 | { 1892 | QString bodyTypeString; 1893 | bodyTypeString += "Type: " + networkBodyType2String(body.first) + "\n"; 1894 | bodyTypeString += "Data: \n"; 1895 | 1896 | QString bodyDataString; 1897 | if (body.first == HttpRequest::MultiPart) { 1898 | QDebug d(&bodyDataString); 1899 | d << body.second; 1900 | } 1901 | else if (body.first == HttpRequest::FileMap) { 1902 | const auto &fileMap = body.second.value>(); 1903 | for (const auto &each : fileMap.toStdMap()) { 1904 | const QString &key = each.first; 1905 | const QString &filePath = each.second; 1906 | bodyDataString += key + ": " + filePath + "\n"; 1907 | } 1908 | } 1909 | else if (body.first == HttpRequest::FormData) { 1910 | const auto &formDataMap = body.second.value>(); 1911 | for (const auto &each : formDataMap.toStdMap()) { 1912 | const QString &key = each.first; 1913 | const QString &value = each.second.toString(); 1914 | bodyDataString += key + ": " + value + "\n"; 1915 | } 1916 | } 1917 | else if (body.first == HttpRequest::X_Www_Form_Urlencoded || 1918 | body.first == HttpRequest::Raw || 1919 | body.first == HttpRequest::Raw_Json) 1920 | { 1921 | bodyDataString += body.second.toByteArray(); 1922 | } 1923 | 1924 | if (bodyDataString.isEmpty()) { 1925 | bodyDataString = "null"; 1926 | } 1927 | 1928 | bodyDataString = lineIndent(bodyDataString, "=> "); 1929 | 1930 | QString bodyString = bodyTypeString + bodyDataString; 1931 | if (bodyString.at(bodyString.count()-1) == '\n') { 1932 | bodyString.chop(1); 1933 | } 1934 | 1935 | return bodyString; 1936 | } 1937 | 1938 | inline QString networkOperation2String(QNetworkAccessManager::Operation o) 1939 | { 1940 | static QMap verbMap = 1941 | { 1942 | {QNetworkAccessManager::HeadOperation, "HEAD"}, 1943 | {QNetworkAccessManager::GetOperation, "GET"}, 1944 | {QNetworkAccessManager::PostOperation, "POST"}, 1945 | {QNetworkAccessManager::PutOperation, "PUT"}, 1946 | }; 1947 | 1948 | return verbMap.value(o, ""); 1949 | } 1950 | } 1951 | 1952 | #define HTTPRESPONSE_DECLARE_METATYPE(...) \ 1953 | Q_DECLARE_METATYPE(std::function) 1954 | 1955 | HTTPRESPONSE_DECLARE_METATYPE(void) 1956 | HTTPRESPONSE_DECLARE_METATYPE(QByteArray) 1957 | HTTPRESPONSE_DECLARE_METATYPE(QString) 1958 | HTTPRESPONSE_DECLARE_METATYPE(QVariantMap) 1959 | HTTPRESPONSE_DECLARE_METATYPE(QJsonObject) 1960 | HTTPRESPONSE_DECLARE_METATYPE(QNetworkReply*) 1961 | HTTPRESPONSE_DECLARE_METATYPE(qint64, qint64) 1962 | HTTPRESPONSE_DECLARE_METATYPE(QNetworkReply::NetworkError) 1963 | HTTPRESPONSE_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator*) 1964 | HTTPRESPONSE_DECLARE_METATYPE(QUrl) 1965 | HTTPRESPONSE_DECLARE_METATYPE(QList) 1966 | HTTPRESPONSE_DECLARE_METATYPE(QAuthenticator*) 1967 | HTTPRESPONSE_DECLARE_METATYPE(QList) 1968 | HTTPRESPONSE_DECLARE_METATYPE(QMap) 1969 | 1970 | #endif // QTHUB_COM_HTTPCLIENT_HPP 1971 | -------------------------------------------------------------------------------- /src/QtNetworkService.pri: -------------------------------------------------------------------------------- 1 | #********************************************************** 2 | #Author: Qt君 3 | #微信公众号: Qt君 4 | #Website: qthub.com 5 | #Email: 2088201923@qq.com 6 | #QQ交流群: 732271126 7 | #Source Code: https://github.com/aeagean/QtNetworkService 8 | #LISCENSE: MIT 9 | #********************************************************** 10 | INCLUDEPATH += $$PWD/ 11 | 12 | HEADERS += \ 13 | $$PWD/HttpClient.h 14 | 15 | QT += network 16 | 17 | CONFIG += c++11 18 | --------------------------------------------------------------------------------