Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Key internal components:
- Strategies: `requeststrategies.h/.cpp` (IRetryStrategy, IRedirectHandler, ISslPolicy)
- Cross-cutting: `qtcompat.h` (Qt version adapters), `progressthrottle.h/.cpp` (progress rate-limiter), `networkrequestregistry.h/.cpp` (self-registering factory)
- Features (v2.3): `environment.cpp` (env substitution), `environmentstore.cpp` (named envs), `oauth2tokencache.cpp` (token cache), `collectionmodel.cpp` (collection tree), `postmanconverter.cpp` (Postman v2.1 I/O)
- Infrastructure: `networkrequestmanager.cpp`, `networkrequestrunnable.*`, `networkaccessmanagerpool.*`, `memorymappedfile.*`, `networkrequestutility.*`, `networkreply.cpp`, `networkcookiejar.*`, `sharedcookiejar.h`
- Infrastructure: `networkrequestmanager.cpp`, `networkrequestrunnable.*`, `networkaccessmanagerpool.*`, `memorymappedfile.*`, `networkrequestutils.*`, `networkreply.cpp`, `networkcookiejar.*`, `sharedcookiejar.h`
- `test/` — Qt Test unit tests (7 suites: UnitTests, BuilderTests, AuthTests, CollectionTests, UtilityTests, DownloaderTests, UiTests)
- `samples/networkrequesttool/` — GUI demo
- `samples/networkdownloader/` — download manager app
Expand Down
60 changes: 30 additions & 30 deletions docs/refactor-network-request-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@

1. **代码重复率过高**:`start()` 和 `onFinished()` 约 60%-70% 逻辑在 4 个子类中重复(约 450 行重复代码)
2. **重定向 / 重试 / SSL 错误处理散落多处**:同一逻辑在 5 个位置独立实现,行为一致性靠人工保证
3. **工厂方法违反开闭原则**:`NetworkRequestFactory::create()` 使用硬编码 `switch-case`,新增类型必须修改工厂
4. **`MTDownloadRequest` 多阶段状态隐式编码**:`m_pNetworkReply` 在三个阶段语义不同,状态转换不可见
3. **工厂方法违反开闭原则**:旧 `NetworkRequest` 类内使用硬编码 `switch-case` 创建请求,新增类型必须修改工厂
4. **`MTDownloadRequest` 多阶段状态隐式编码**:`m_networkReply` 在三个阶段语义不同,状态转换不可见
5. **Qt 版本兼容代码散落**:`#if QT_VERSION` 在约 15 处出现,新增 Qt6 支持时修改成本高

---
Expand Down Expand Up @@ -210,10 +210,10 @@ protected:
void populateResponseHeaders(QNetworkReply* reply);

// 持有
RequestContextPtr m_upContext;
QSharedPointer<ResponseResult> m_spResult;
QNetworkAccessManager* m_pNetworkManager = nullptr;
QNetworkReply* m_pNetworkReply = nullptr;
RequestContextPtr m_context;
QSharedPointer<ResponseResult> m_result;
QNetworkAccessManager* m_networkManager = nullptr;
QNetworkReply* m_networkReply = nullptr;

std::unique_ptr<IRetryStrategy> m_retryStrategy;
std::unique_ptr<IRedirectHandler> m_redirectHandler;
Expand All @@ -240,45 +240,45 @@ void NetworkRequest::start()
}

// 步骤 3: 获取 NAM + 应用公共配置(代理/Cookie/SSL/头/超时)
if (nullptr == m_pNetworkManager)
m_pNetworkManager = NetworkRequestManager::acquireThreadNam();
applyCommonConfig(m_pNetworkManager);
if (nullptr == m_networkManager)
m_networkManager = NetworkRequestManager::acquireThreadNam();
applyCommonConfig(m_networkManager);

// 步骤 4: 子类构建请求
QNetworkRequest req = buildRequest();

// 步骤 5: 子类发送请求
m_pNetworkReply = executeRequest(req);
m_networkReply = executeRequest(req);

// 步骤 6: 统一连接核心信号
connectCommonSignals(m_pNetworkReply);
connectCommonSignals(m_networkReply);

// 步骤 7: 子类连接额外信号
connectExtraSignals(m_pNetworkReply);
connectExtraSignals(m_networkReply);

// 步骤 8: 启动心跳(idle/transfer timeout)
startHeartbeatMonitor();
}

void NetworkRequest::onFinished()
{
// 该槽连接到 m_pNetworkReply->finished,子类不再直接处理
if (!m_pNetworkReply)
// 该槽连接到 m_networkReply->finished,子类不再直接处理
if (!m_networkReply)
return;

// 步骤 1: 错误处理(包含重试 + 重定向)
if (handleError(m_pNetworkReply))
if (handleError(m_networkReply))
return;

// 步骤 2: 子类处理响应体
if (!processResponseBody(m_pNetworkReply))
if (!processResponseBody(m_networkReply))
return;

// 步骤 3: 填充响应头
populateResponseHeaders(m_pNetworkReply);
populateResponseHeaders(m_networkReply);

// 步骤 4: 清理
cleanupReply(m_pNetworkReply);
cleanupReply(m_networkReply);

// 步骤 5: 子类后置钩子
onPostFinish();
Expand All @@ -301,19 +301,19 @@ protected:
}

QNetworkReply* executeRequest(QNetworkRequest& req) override {
switch (m_upContext->type) {
case RequestType::Get: return m_pNetworkManager->get(req);
case RequestType::Post: return m_pNetworkManager->post(req, buildBody());
case RequestType::Put: return m_pNetworkManager->put(req, buildBody());
case RequestType::Delete: return m_pNetworkManager->deleteResource(req);
case RequestType::Head: return m_pNetworkManager->head(req);
default: return m_pNetworkManager->sendCustomRequest(req, verb());
switch (m_context->type) {
case RequestType::Get: return m_networkManager->get(req);
case RequestType::Post: return m_networkManager->post(req, buildBody());
case RequestType::Put: return m_networkManager->put(req, buildBody());
case RequestType::Delete: return m_networkManager->deleteResource(req);
case RequestType::Head: return m_networkManager->head(req);
default: return m_networkManager->sendCustomRequest(req, verb());
}
}

bool processResponseBody(QNetworkReply* reply) override {
m_spResult->responseData = reply->readAll();
m_spResult->responseBody = m_spResult->responseData;
m_result->responseData = reply->readAll();
m_result->responseBody = m_result->responseData;
return true;
}
// 不覆写 onPreStart / connectExtraSignals / onPostFinish — 使用默认空实现
Expand Down Expand Up @@ -707,7 +707,7 @@ Phase 1 Phase 2 Phase 3 Phase 4 Phase 5

### 6.6 Phase 4 — MTDownloadRequest 状态机重构(预计 2 天)

**目标**:引入显式状态机,消除 `m_pNetworkReply` 语义歧义和隐式阶段转换。
**目标**:引入显式状态机,消除 `m_networkReply` 语义歧义和隐式阶段转换。

| 任务 | 工时 |
|:---|:---|
Expand Down Expand Up @@ -737,7 +737,7 @@ Phase 1 Phase 2 Phase 3 Phase 4 Phase 5
|:---|:---|
| 5.1 实现 `NetworkRequestRegistry` | 0.3 天 |
| 5.2 各子类添加静态自注册代码 | 0.3 天 |
| 5.3 `NetworkRequestFactory::create()` 改为调用 Registry | 0.2 天 |
| 5.3 移除旧 `NetworkRequestFactory`,请求创建统一委托 `NetworkRequestRegistry` | 0.2 天 |
| 5.4 删除旧的 switch-case | 0.1 天 |
| 5.5 回归测试 + 验证动态加载场景 | 0.1 天 |

Expand Down Expand Up @@ -857,5 +857,5 @@ Week 1 Week 2
| SSL 错误处理 | 2 处重复 | `ISslPolicy` 一处实现 |
| Qt 版本兼容 | ~15 处 `#if` 散落 | `QtCompat` 命名空间一处维护 |
| 工厂扩展性 | 修改 switch-case | 自注册,不修改核心代码 |
| MTDownloadRequest 状态 | 隐式,`m_pNetworkReply` 多语义 | 显式状态机,每状态独立类 |
| MTDownloadRequest 状态 | 隐式,`m_networkReply` 多语义 | 显式状态机,每状态独立类 |
| 新增请求类型成本 | ~120 行 + 改工厂 | ~30 行 + 一行注册代码 |
22 changes: 11 additions & 11 deletions include/networkrequestevent.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ namespace QtNetworkRequest
return iter->second;
}

int nEventType = QEvent::registerEventType();
s_mapUserEvent[eventName] = nEventType;
return nEventType;
int eventType = QEvent::registerEventType();
s_mapUserEvent[eventName] = eventType;
return eventType;
}

// Keep old spelling for backward compatibility
Expand All @@ -70,25 +70,25 @@ namespace QtNetworkRequest
class ReplyResultEvent : public QEvent
{
public:
ReplyResultEvent() : QEvent(QEvent::Type(NetworkEvent::ReplyResult)), bDestroyed(true) {}
ReplyResultEvent() : QEvent(QEvent::Type(NetworkEvent::ReplyResult)), isDestroyed(true) {}

QSharedPointer<ResponseResult> response;
bool bDestroyed;
bool isDestroyed;
};

// Download/Upload progress event
class NetworkProgressEvent : public QEvent
{
public:
NetworkProgressEvent() : QEvent(QEvent::Type(NetworkEvent::NetworkProgress)), bDownload(true), uiId(0), uiBatchId(0), iBytes(0), iTotalBytes(0)
NetworkProgressEvent() : QEvent(QEvent::Type(NetworkEvent::NetworkProgress)), isDownload(true), requestId(0), batchId(0), transferredBytes(0), totalBytes(0)
{
}

bool bDownload;
quint64 uiId;
quint64 uiBatchId;
qint64 iBytes;
qint64 iTotalBytes;
bool isDownload;
quint64 requestId;
quint64 batchId;
qint64 transferredBytes;
qint64 totalBytes;
};
}

Expand Down
36 changes: 18 additions & 18 deletions include/networkrequestmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,41 +106,41 @@ namespace QtNetworkRequest
std::shared_ptr<NetworkReply> postRequest(std::unique_ptr<RequestContext> context);

// Asynchronously execute batch request tasks (requests in same batch will be bound to same NetworkReply)
std::shared_ptr<NetworkReply> postBatchRequest(BatchRequestPtrTasks&& tasks, quint64 &uiBatchId);
std::shared_ptr<NetworkReply> postBatchRequest(BatchRequestPtrTasks&& tasks, quint64 &batchId);

// Synchronously execute single request task (returns false if url is invalid or no idle thread to handle)
// By default, synchronous mode blocks user interaction to avoid callback object not existing during callback. If set to non-blocking, caller needs to ensure callback lifecycle
bool sendRequest(std::unique_ptr<RequestContext> context, ResponseCallBack callback, bool bBlockUserInteraction = true);
bool sendRequest(std::unique_ptr<RequestContext> context, ResponseCallBack callback, bool blockUserInteraction = true);

// Stop all request tasks (async requests only)
void stopAllRequest();
// Stop batch request tasks with specified batchid (async requests only)
void stopBatchRequests(quint64 uiBatchId);
void stopBatchRequests(quint64 batchId);
// Stop specific request task (async requests only)
void stopRequest(quint64 uiTaskId);
void stopRequest(quint64 taskId);
// Stop all requests of specific session (async requests only)
void stopSessionRequest(quint64 uiSessionId);
void stopSessionRequest(quint64 sessionId);

public:
// Set maximum thread count for thread pool (1-100, default is system CPU core count)
bool setMaxThreadCount(int iMax);
bool setMaxThreadCount(int maxConcurrent);
int maxThreadCount();

quint64 nextSessionId();

Q_SIGNALS:
void errorMessage(const QString &error);
void batchRequestFinished(quint64 uiBatchId, bool bAllSuccess);
void batchRequestFinished(quint64 batchId, bool allSuccess);

public Q_SLOTS:
void onResponse(QSharedPointer<QtNetworkRequest::ResponseResult> rsp);
// Destroys a runnable on the main thread once its run() has fully
// returned (see NetworkRequestRunnable::finished). Sole owner of the
// runnable's lifetime end for cancelled-while-running requests.
void onRunnableFinished(quint64 uiRequestId);
void onRunnableFinished(quint64 requestId);

public:
bool event(QEvent *pEvent) Q_DECL_OVERRIDE;
bool event(QEvent *event) Q_DECL_OVERRIDE;

private:
explicit NetworkRequestManager(QObject *parent = 0);
Expand All @@ -153,20 +153,20 @@ namespace QtNetworkRequest

bool startAsRunnable(std::unique_ptr<RequestContext> request);

// bDownload(false: upload)
void updateProgress(quint64 uiRequestId, quint64 uiBatchId,
qint64 iBytes, qint64 iTotalBytes, bool bDownload);
// isDownload(false: upload)
void updateProgress(quint64 requestId, quint64 batchId,
qint64 transferredBytes, qint64 totalBytes, bool isDownload);

private:
QScopedPointer<NetworkRequestManagerPrivate> d_ptr;

static std::atomic<bool> ms_bIntialized;
static std::atomic<bool> ms_bUnIntializing;
static ProxyConfig ms_globalProxy;
static QScopedPointer<QNetworkCookieJar> ms_spCookieJar;
static std::atomic<bool> s_isInitialized;
static std::atomic<bool> s_isUninitializing;
static ProxyConfig s_globalProxy;
static QScopedPointer<QNetworkCookieJar> s_cookieJar;
#ifndef QT_NO_SSL
static SslConfig ms_globalSslConfig;
static QMutex ms_globalSslConfigMutex;
static SslConfig s_globalSslConfig;
static QMutex s_globalSslConfigMutex;
#endif
};
}
78 changes: 41 additions & 37 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,45 @@

add_library(QNetworkRequest SHARED
# ── Implementation ───────────────────────────────────────────────────────
memorymappedfile.cpp
networkaccessmanagerpool.cpp
networkcommonrequest.cpp
networkcookiejar.cpp
networkdownloadrequest.cpp
networkmtdownloadrequest.cpp
networkmtdownloadrequest_states.cpp
networkreply.cpp
networkrequest.cpp
networkrequestmanager.cpp
networkrequestrunnable.cpp
networkuploadrequest.cpp
networkrequestutility.cpp
progressthrottle.cpp
networkrequestregistry.cpp
requeststrategies.cpp
environment.cpp
oauth2tokencache.cpp
collectionmodel.cpp
postmanconverter.cpp
environmentstore.cpp
infra/memorymappedfile.cpp
core/networkaccessmanagerpool.cpp
requests/networkcommonrequest.cpp
infra/networkcookiejar.cpp
requests/networkdownloadrequest.cpp
requests/networkmtdownloadrequest.cpp
requests/networkmtdownloadrequest_states.cpp
core/networkreply.cpp
requests/networkrequest.cpp
core/networkrequestmanager.cpp
core/networkrequestrunnable.cpp
requests/networkuploadrequest.cpp
features/networkrequestutils.cpp
core/progressthrottle.cpp
core/networkrequestregistry.cpp
infra/requeststrategies.cpp
features/environment.cpp
features/oauth2tokencache.cpp
features/collectionmodel.cpp
features/postmanconverter.cpp
features/environmentstore.cpp

# ── Private headers (AUTOMOC) ────────────────────────────────────────────
memorymappedfile.h
networkaccessmanagerpool.h
networkcommonrequest.h
networkcookiejar.h
networkdownloadrequest.h
networkmtdownloadrequest.h
networkmtdownloadrequest_p.h
networkrequest.h
networkrequestrunnable.h
networkuploadrequest.h
networkrequestutility.h
sharedcookiejar.h
networkrequestregistry.h
progressthrottle.h
qtcompat.h
requeststrategies.h
infra/memorymappedfile.h
core/networkaccessmanagerpool.h
requests/networkcommonrequest.h
infra/networkcookiejar.h
requests/networkdownloadrequest.h
requests/networkmtdownloadrequest.h
requests/networkmtdownloadrequest_p.h
requests/networkrequest.h
core/networkrequestrunnable.h
requests/networkuploadrequest.h
features/networkrequestutils.h
infra/sharedcookiejar.h
core/networkrequestregistry.h
core/progressthrottle.h
core/qtcompat.h
infra/requeststrategies.h

# ── Public headers (AUTOMOC) ─────────────────────────────────────────────
${CMAKE_SOURCE_DIR}/include/networkrequestmanager.h
Expand Down Expand Up @@ -80,6 +80,10 @@ target_include_directories(QNetworkRequest
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/core
${CMAKE_CURRENT_SOURCE_DIR}/requests
${CMAKE_CURRENT_SOURCE_DIR}/features
${CMAKE_CURRENT_SOURCE_DIR}/infra
)

# ── Dependencies ─────────────────────────────────────────────────────────────
Expand Down
Loading
Loading