codetether_agent/provider/shared_http.rs
1//! Process-wide shared [`reqwest::Client`].
2//!
3//! Each call to [`reqwest::Client::new`] loads the system TLS root store
4//! (hundreds of certs), builds a fresh TLS config, and allocates a new
5//! connection pool. Across ~10 providers that's hundreds of milliseconds
6//! of startup cost on first use plus duplicated pools that cannot share
7//! keep-alive connections.
8//!
9//! [`shared_client`] returns a single `Client` that every provider which
10//! does not need custom builder options can clone (the `Client` is
11//! internally reference-counted, so clones are O(1) and share the
12//! underlying connection pool).
13//!
14//! Providers that need custom timeouts, proxies, or TLS settings
15//! (`openai`, `vertex_*`, `gemini_web`, `openrouter`) continue to use
16//! `Client::builder()` directly — those configurations cannot be
17//! meaningfully shared anyway.
18
19use once_cell::sync::Lazy;
20use reqwest::Client;
21
22static SHARED: Lazy<Client> = Lazy::new(|| {
23 Client::builder()
24 // Enable HTTP/2 adaptive window and prior-knowledge upgrades
25 // where the server supports them. This is reqwest's default
26 // but pinning it here documents the expectation.
27 .use_rustls_tls()
28 // Keep connections alive across provider calls so repeated
29 // streaming completions hit the same TCP/TLS session.
30 .pool_idle_timeout(Some(std::time::Duration::from_secs(90)))
31 .pool_max_idle_per_host(8)
32 .build()
33 .unwrap_or_else(|_| Client::new())
34});
35
36/// Return a handle to the process-wide shared `reqwest::Client`.
37///
38/// `Client` is cheap to clone — it wraps an `Arc` internally — so callers
39/// should `.clone()` the returned reference into their own state.
40pub fn shared_client() -> &'static Client {
41 &SHARED
42}