基于 Qt6 和 libcurl 的现代 C++ 网络库,提供高性能、类型安全的 HTTP Core API 与可选 Extras / 显式 Extras / Preview。
QCurl 1.0.0 first stable 的稳定承诺范围以默认安装面为准:QCURL_INSTALL_HEADERS + QCurlConfig.h。这些头文件构成 Core API,进入默认 find_package(QCurl) / QCurl::QCurl consumer contract。shared library 与 static library 都属于本次 1.0.0 first stable 发布形态;whole project、WebSocket 和 Diagnostics 不随 Core 一起宣布 Stable。
| 层级 | 发布含义 | 当前范围 |
|---|---|---|
| Core | 默认安装,作为 1.0.0 first stable 维护 API / ABI | QCNetworkAccessManager、QCCookie、QCCookieAsyncResult、QCNetworkRequest、QCNetworkRequestConfig、QCNetworkReply、TLS / proxy / timeout / retry / redirect / transfer 配置、HTTP method / version / error / priority、lane-aware scheduler、cache policy type header、Cache lookup concrete API、Multipart builder、QCNetworkLogger、QCNetworkDefaultLogger、QCNetworkCancelToken、Middleware base、ConnectionPool 管理面 |
| Blocking Extras | 显式安装,提供同步 value-result 工具;不混入默认 Core | QCBlockingNetworkClient、QCBlockingNetworkResult、QCBlockingCookieStore |
| Test Support | 显式安装,用于测试支持,不作为生产运行时网络栈能力表述 | QCNetworkMockHandler、QCNetworkCapturedRequest、QCNetworkTestSupport |
| Other Extras / Preview | 显式安装或条件安装;不属于默认 Core 稳定承诺 | Diagnostics、Middleware Extras、WebSocket |
除非文档明确标注为 Core,示例中引用 QCURL_INSTALL_HEADERS_EXTRAS 的头文件时,都应视为显式 opt-in 的非默认发行面。完整边界见 docs/arch/public-header-boundary.md、docs/arch/1.0-first-stable-release-contract.md 与 docs/arch/1.0-first-stable-readiness-report.md。
| 现代化架构 | Core HTTP | 可选扩展 | Qt 友好 |
|---|---|---|---|
| CMake + RAII + C++17 | HTTP/1.1、HTTP/2、HTTP/3 capability | 显式 Extras / Preview | QObject 线程归属与事件循环合同 |
- CMake 构建系统 - 跨平台支持,自动依赖检测
- 统一 Reply 架构 -
QCNetworkReply作为 Core 异步响应入口,统一承载状态、信号、读取和错误信息 - RAII 资源管理 - Core 资源生命周期由 public API 合同和测试门禁约束,调用方无需直接管理 libcurl handle
- C++17 特性 -
std::optional、std::chrono、[[nodiscard]]、enum class - HTTP/1.1、HTTP/2、HTTP/3 capability - HTTP/3 取决于运行时 libcurl / QUIC backend
- SSL/TLS - 可配置证书验证、客户端证书、CA 路径
- 代理支持 - HTTP、HTTPS、SOCKS4/4A、SOCKS5
- Canonical Request API -
QCNetworkRequest+QCNetworkAccessManager::head()/get()/post()/put()/patch()一套入口覆盖配置与发送 - 请求对象配置 -
QCNetworkRequest::setRawHeader()/setTimeout()/setPriority()/setLane()支持链式配置;QCNetworkRedirectConfig与QCNetworkTransferConfig聚合重定向和传输配置 - 请求重试 - 指数退避算法,自动处理临时性错误
- lane-aware 调度 - lane reservation + DRR 公平调度 + 按 lane 精准取消
- 缓存策略类型 -
QCNetworkCachePolicy是QCNetworkRequest的 Core 配置类型 - Cache lookup API -
QCNetworkCache、QCNetworkMemoryCache、QCNetworkDiskCache提供lookup(url, ReadMode),返回Miss / FreshHit / StaleHit - Multipart/form-data builder -
QCMultipartFormData/QCNetworkMultipartBody生成 body,再通过post()发送 - 日志接口 -
QCNetworkLogger提供 Core 级日志抽象与 debug trace 脱敏入口 - 默认日志实现 -
QCNetworkDefaultLogger提供 Core 级默认 logger helper - 取消令牌 -
QCNetworkCancelToken提供 reply-level 批量取消和自动超时取消 - Middleware base -
QCNetworkMiddleware作为 Core 拦截与观测基类进入默认安装面;通用具体 middleware 通过 Other Extras opt-in 使用 - ConnectionPool 管理面 - 连接池配置、统计和资源控制接口使用 accessor / shared-data API
- 流式下载/上传 -
QCNetworkDownloadToDeviceJob与 manager-levelpost()/put()raw-body device overload 支持大文件 - 断点续传 -
QCNetworkResumableDownloadJob基于 HTTP Range 请求恢复下载 - Core cookie model -
QCCookie是 Core public cookie 值类型;默认 consumer 不需要 QtNetwork cookie 类型 - Cookie async result -
QCCookieOperationResult/QCCookieExportResult是 manager cookie async signal 与QFuture的 Core 值结果
- 同步 value-result client -
QCBlockingNetworkClient/QCBlockingNetworkResult通过显式BlockingExtrasDevelopment安装,不随默认 Core 安装。 - 受限内存响应体 -
QCBlockingRequestOptions::maxInMemoryBodyBytes()默认限制内存响应体;超过上限返回NetworkError::BodyTooLarge。 - 大响应下载 - 大响应使用
QCBlockingNetworkClient::downloadToDevice()写入调用方提供的QIODevice,body()保持为空,bytesReceived()记录实际接收字节数。 - 诊断错误边界 - Blocking Extras 使用
BodyTooLarge、OutputDeviceError、InputDeviceError、ReplayNotSupported等明确错误;curl code 只通过diagnosticCurlCode()作为辅助诊断,不作为主判断 API。 - Cookie snapshot / delta -
QCBlockingCookieStore提供 Blocking Extras cookie 边界,不访问 live manager cookie store。
- MockHandler Test Support -
QCNetworkMockHandler、QCNetworkCapturedRequest与QCNetworkTestSupport通过显式TestSupportDevelopment安装,供测试程序 opt-in 使用。
- Diagnostics 扩展诊断 -
QCNetworkDiagnostics通过显式OtherExtrasDevelopment安装;QtNetwork 依赖只由QCurl::OtherExtras承担,ping/traceroute与detailsschema 仍不作为默认 Core 稳定合同。 - Middleware Extras -
QCNetworkMiddlewareExtras通过显式OtherExtrasDevelopment安装;默认 Core 只承诺QCNetworkMiddlewarebase。 - WebSocket - 客户端实现包含压缩、自动重连和连接池能力,条件进入 Other Extras;当前仍不属于默认 Core install surface。
性能数字需要绑定具体 benchmark 版本、依赖版本、网络环境和日期。未绑定证据的数字不应作为 1.0.0 first stable 稳定发布承诺。
| 依赖 | 版本要求 | 说明 |
|---|---|---|
| CMake | 3.16+ | 构建系统 |
| Qt6 | 6.2+ | Core consumer 需要 QtCore;Other Extras diagnostics 需要 QtNetwork |
| libcurl | 7.85.0+ | WebSocket 需 7.86.0+;HTTP/3 推荐 8.16.0+ 且带 QUIC backend |
| 编译器 | C++17 | GCC 11+、Clang 14+、MSVC 2019+ |
git clone https://github.com/SunRain/QCurl.git
cd QCurl
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"
cmake --install build --prefix "$PWD/stage"发布合同提示:
- 1.0.0 first stable 只承诺 Core install surface。
- Blocking Extras / Test Support / Other Extras 需要发行包显式安装对应 component,不随默认 Core 隐式安装。
- 当前 lane-aware scheduler 已纳入
QCurl 1.0.0 / SOVERSION 1first stable Core 合同;本发布线不提供旧 2.x/3.x 兼容层。
#include <QCNetworkAccessManager.h>
#include <QCNetworkRequest.h>
QCurl::QCNetworkAccessManager manager;
QCurl::QCNetworkRequest request(QUrl("https://api.example.com/data"));
request.setRawHeader("Authorization", "Bearer token")
.setTimeout(std::chrono::seconds(30));
auto *reply = manager.get(request);
connect(reply, &QCurl::QCNetworkReply::finished, [reply]() {
if (reply->error() == QCurl::NetworkError::NoError) {
if (const auto data = reply->readAll(); data.has_value()) {
qDebug() << "Response:" << *data;
}
}
reply->deleteLater();
});说明:WebSocket 使用
QCURL_INSTALL_HEADERS_EXTRAS中的扩展头。 它可以作为 Preview 功能使用,但不属于 1.0.0 first stable 承诺。
#include <QCWebSocket.h>
#include <QCWebSocketCompressionConfig.h>
#include <QCWebSocketReconnectPolicy.h>
QCurl::QCWebSocketOptions options;
options.setCompressionConfig(QCurl::QCWebSocketCompressionConfig::defaultConfig());
options.setReconnectPolicy(QCurl::QCWebSocketReconnectPolicy::standardReconnect());
auto *socket = new QCurl::QCWebSocket(QUrl("wss://echo.websocket.org"), options);
connect(socket, &QCurl::QCWebSocket::connected, [socket]() {
socket->sendTextMessage("Hello WebSocket!");
});
connect(socket, &QCurl::QCWebSocket::textMessageReceived, [](const QString &msg) {
qDebug() << "Received:" << msg;
});
socket->open();QCMultipartFormData.h 属于 Core install surface,默认 installed consumer 可以直接使用该 builder。
#include <QCMultipartFormData.h>
#include <QCNetworkMultipartBody.h>
QCurl::QCMultipartFormData formData;
formData.addTextField("userId", "12345");
formData.addFileField("avatar", "/path/to/photo.jpg");
QCurl::QCNetworkRequest uploadRequest(QUrl("https://api.example.com/upload"));
auto body = QCurl::QCNetworkMultipartBody::fromFormData(formData);
uploadRequest.setRawHeader("Content-Type", body.contentType());
auto *reply = manager.post(uploadRequest, body.data());QCNetworkBody 会随请求体保存匹配的 Content-Type。post() / put() /
patch() 接收 QCNetworkBody 时,若请求尚未显式设置 Content-Type,会自动补齐;若已设置,
则尊重请求里的显式值。
#include <QCNetworkBody.h>
QCurl::QCNetworkRequest formRequest(QUrl("https://api.example.com/form"));
auto formBody = QCurl::QCNetworkBody::fromFormUrlEncoded(
QList<QPair<QString, QString>>{
{QStringLiteral("tag"), QStringLiteral("one")},
{QStringLiteral("tag"), QStringLiteral("two")},
});
auto *formReply = manager.post(formRequest, formBody);调度配置入口在 QCNetworkAccessManager 上;请求 lane 使用 typed key,未注册 lane 会
fail-closed。
#include <QCNetworkLaneKey.h>
#include <QCNetworkSchedulerPolicy.h>
QCurl::QCNetworkSchedulerPolicy policy = QCurl::QCNetworkSchedulerPolicy::defaultPolicy();
policy.setMaxConcurrentRequests(6);
policy.setMaxRequestsPerHost(2);
QString schedulerError;
QCurl::QCNetworkSchedulerPolicy::LaneConfig controlLane;
controlLane.setWeight(3);
controlLane.setQuantum(1);
controlLane.setReservedGlobal(1);
controlLane.setReservedPerHost(1);
if (!policy.setLaneConfig(QCurl::QCNetworkLaneKey::control(), controlLane, &schedulerError)) {
qWarning() << schedulerError;
}
QCurl::QCNetworkSchedulerPolicy::LaneConfig transferLane;
transferLane.setWeight(1);
transferLane.setQuantum(1);
if (!policy.setLaneConfig(QCurl::QCNetworkLaneKey::transfer(), transferLane, &schedulerError)) {
qWarning() << schedulerError;
}
if (!manager.setSchedulerPolicy(policy, &schedulerError)) {
qWarning() << schedulerError;
}
QCurl::QCNetworkRequest controlRequest(QUrl("https://api.example.com/manifest"));
controlRequest.setLane(QCurl::QCNetworkLaneKey::control())
.setPriority(QCurl::QCNetworkRequestPriority::High);
QCurl::QCNetworkRequest transferRequest(QUrl("https://cdn.example.com/chunk.bin"));
transferRequest.setLane(QCurl::QCNetworkLaneKey::transfer())
.setPriority(QCurl::QCNetworkRequestPriority::Low);
const auto stats = manager.schedulerStatistics();
qDebug() << "pending=" << stats.pendingRequests()
<< "running=" << stats.runningRequests();
const auto cancelResult = manager.cancelLaneRequests(
QCurl::QCNetworkLaneKey::transfer(),
QCurl::QCNetworkAccessManager::SchedulerCancelScope::PendingAndRunning);
if (!cancelResult.isSuccess()) {
qWarning() << cancelResult.error();
}简要提示:
lane可以理解成“请求车道”:先按车道分组,再在车道内按优先级排序。Critical现在只影响同一条 lane 内的启动顺序;若需要给控制类请求留保底名额,请使用 lane reservation。QCNetworkLaneKey::fromName(name, &lane, &error)可创建自定义 lane;解析失败不会返回 invalid sentinel,成功后必须先写入QCNetworkSchedulerPolicy。cancelLaneRequests()返回QCNetworkLaneCancelResult;PendingOnly只清 pending/deferred,PendingAndRunning会连 running 一并取消。- scheduler policy 和统计接口必须在 manager owner thread 调用;跨线程配置请显式投递到 owner thread。
完整说明、请求时序图和 Control / Transfer / Background 配置建议统一参考:
docs/user/lane-scheduler.md
性能数字只在绑定具体 benchmark 版本、依赖版本、网络环境和日期时才作为发布证据。
当前 README 不把固定延迟或吞吐数字写成 1.0.0 first stable 稳定承诺。
性能回归与能力证据以以下入口为准:
docs/reference/performance.mddocs/reference/benchmarks.mdtests/libcurl_consistency/run_gate.py --suite all --with-ext --build
测试运行与门禁以 docs/dev/build-and-test.md 为准。发布前使用 full release gate、shared/static public-api gate、ABI gate、metadata scan 和 git diff --check 形成可复验结果。
本 README 不维护固定测试数量、通过率或一次性 gate 输出;这些数字必须绑定具体 build、依赖版本、环境和日期后,放入维护者证据文档或 CI artifacts。
| 文档 | 说明 |
|---|---|
| docs/README.md | 文档入口(按读者角色分层) |
| SYSTEM_DOCUMENTATION.md | 维护者 Architecture overview |
| examples/README.md | 示例集合与运行方式 |
find_package(QCurl CONFIG REQUIRED)
target_link_libraries(your_app PRIVATE QCurl::QCurl)使用 staging prefix 验证独立 consumer 时:
cmake -S your-app -B build-your-app -DCMAKE_PREFIX_PATH=/path/to/QCurl/stage
cmake --build build-your-app默认构建发布 shared library。Static library 需要显式 opt-in:
cmake -S . -B build-static -DCMAKE_BUILD_TYPE=Release -DQCURL_BUILD_SHARED_LIBS=OFF
cmake --build build-static --target QCurl qcurl_public_api_self_compile
ctest --test-dir build-static -L '^public-api$' --output-on-failure
ctest --test-dir build-static -L '^public-api-slow$' --output-on-failureStatic 路径已纳入 full release gate。正式打包前仍以 scripts/run_release_gate.py --tier full --build-dir build --static-build-dir build-static 的最新输出为准;即使 static gate 通过,也只声明 Core static library ready,不声明 whole project static library ready。
Static consumer 若只使用 QCNetworkRequestPriority 等头文件类型,并需要 Qt 元类型按名称可见,应在 main() 早期调用一次 QCurl::initialize()。shared consumer 通常不需要手动调用;该函数幂等,调用后不会创建网络对象或启动 scheduler。
g++ your_app.cpp $(pkg-config --cflags --libs qcurl) -o your_app欢迎 Pull Request!
- 贡献指南:
CONTRIBUTING.md - 行为准则:
CODE_OF_CONDUCT.md - 安全策略:
SECURITY.md - 支持与反馈:
SUPPORT.md
MIT License - 自由使用、修改、分发
QCurl - Qt6 + libcurl Core HTTP library