Skip to content

perf: cache reqwest::Client globally for TCP connection reuse#1303

Open
lennney wants to merge 2 commits into
BigPizzaV3:mainfrom
lennney:perf/reqwest-cache
Open

perf: cache reqwest::Client globally for TCP connection reuse#1303
lennney wants to merge 2 commits into
BigPizzaV3:mainfrom
lennney:perf/reqwest-cache

Conversation

@lennney

@lennney lennney commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Cache reqwest::Client in a OnceLock so the first call initialises it and subsequent calls reuse the same connection pool via cheap Arc clone.

Problem: proxied_client() created a new reqwest::Client on every call — fresh connection pool, extra TLS handshake latency per request.

Fix: Global OnceLock<reqwest::Client> — TCP/TLS connections are now reused across upstream API requests. Client is built outside the closure so build failures return Err instead of panicking.

Behavior Change

Previously, requests without a configured user-agent would pass through the original Codex client UA. Now they always use the CodexPlusPlus/X.Y.Z UA. Tests updated to match on prefix.

Compatibility

  • User-agent is first-call-wins (all callers use similar CodexPlusPlus/* UAs)
  • CDP client (cdp.rs) keeps its own builder with no_proxy — unaffected
  • 3 protocol_proxy UA tests relaxed to prefix-match (with cache, exact UA depends on which test initialised first)

Verification

  • 91 tests passing (1 pre-existing failure — also on upstream/main)

@lennney lennney force-pushed the perf/reqwest-cache branch 2 times, most recently from a042c44 to cdac980 Compare July 2, 2026 15:07
@lennney lennney marked this pull request as ready for review July 2, 2026 15:07
Copilot AI review requested due to automatic review settings July 2, 2026 15:07
- OnceLock caching for TCP/TLS connection reuse across all upstream requests
- User-Agent is set per-request via .header(), not baked into the cached client
- Fixes UA first-caller-wins bug where dynamic UAs were silently ignored
- Restore exact UA assertions in protocol_proxy tests (configured vs passthrough)
- Update 12 call sites across model_catalog, stepwise, protocol_proxy, relay_config, update, plugin_marketplace, and ads
@lennney lennney force-pushed the perf/reqwest-cache branch from cdac980 to d52cf73 Compare July 2, 2026 15:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 旨在通过全局缓存 reqwest::Client(使用 OnceLock 惰性初始化)来复用连接池,从而减少每次请求重新建连/TLS 握手带来的额外开销;同时将 User-Agent 从“客户端默认 header”迁移为“每个请求显式设置”。

Changes:

  • http_client::proxied_client 改为全局缓存的 OnceLock<reqwest::Client>,避免重复创建连接池。
  • 将多处调用从 proxied_client(user_agent) 调整为 proxied_client() + 每个请求 .header("User-Agent", ...)
  • 调整 model catalog 读取路径与相关测试,新增/传递 user_agent 参数以便请求侧设置 UA。

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
crates/codex-plus-core/src/http_client.rs 引入全局 OnceLock 缓存 reqwest::Client,不再在 client 上设置默认 UA
crates/codex-plus-core/src/protocol_proxy.rs 上游请求改为复用全局 client,并在请求级别设置 UA
crates/codex-plus-core/src/model_catalog.rs read_codex_model_catalog_from_home / fetch_models_from_source 增加 UA 透传并在请求侧设置
crates/codex-plus-core/src/update.rs 更新检查/下载请求改为复用全局 client,并显式设置 UA
crates/codex-plus-core/src/plugin_marketplace.rs marketplace zip 下载请求复用全局 client,并显式设置 UA
crates/codex-plus-core/src/stepwise.rs stepwise 请求复用全局 client,并显式设置 UA
crates/codex-plus-core/src/relay_config.rs relay 测试请求复用全局 client,并显式设置 UA
crates/codex-plus-core/src/ads.rs ads 拉取请求复用全局 client,并显式设置 UA
crates/codex-plus-core/tests/model_catalog.rs 跟随函数签名变化,为 read_codex_model_catalog_from_home 增加 user_agent 实参
crates/codex-plus-core/tests/protocol_proxy.rs 为 UA 行为相关测试增加/调整注释

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 533 to 535
let ua_value = effective_user_agent(&relay.user_agent, original_user_agent);
let client = crate::http_client::proxied_client()?;
let upstream = match send_upstream_request_for_responses(
Comment on lines 651 to 653
let ua_value = effective_user_agent(&relay.user_agent, original_user_agent);
let client = crate::http_client::proxied_client()?;
let upstream = send_upstream_request(
Comment on lines +698 to +704
let ua_value = effective_user_agent(&relay.user_agent, original_user_agent);
let client = crate::http_client::proxied_client()?;
let upstream = client
.post(chat_completions_url(&relay.base_url))
.bearer_auth(relay.api_key.trim())
.header(reqwest::header::CONTENT_TYPE, "application/json")
.header("User-Agent", &ua_value)
Comment on lines +469 to +472
let mut request = client
.get(&endpoint)
.header(reqwest::header::ACCEPT, "application/json");
.header(reqwest::header::ACCEPT, "application/json")
.header("User-Agent", user_agent);
Comment on lines 524 to 528
.header(reqwest::header::CONTENT_TYPE, "application/json")
.header("User-Agent", "CodexPlusPlus/RelayTest")
.json(&payload)
.send()
.await?;
Comment on lines +1493 to +1494
/// Verify the proxied client sets the default CodexPlusPlus user-agent.
///
…eqwest default

Copilot review identified 6 call sites where effective_user_agent()
returning an empty string would set an empty User-Agent header, which is
worse than not setting one (reqwest would use its default).

Fixes:
- protocol_proxy.rs: 3 call sites — guard with !ua_value.is_empty()
- model_catalog.rs: 1 call site — same guard
- relay_config.rs: 1 call site — add missing UA to /v1 retry request
- tests/protocol_proxy.rs: update outdated comment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants