├── 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 |
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 |
--------------------------------------------------------------------------------