1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! High-level pooled HTTP [`Client`] facade — the capability-gated entry point
//! to the runtime's default client.
//!
//! [`Client`] is the ergonomic, public name for the pooled
//! [`HttpClient`](super::h1::HttpClient): a cheap-clone handle (every field is
//! an [`Arc`](std::sync::Arc)) over a shared connection pool, idle-connection
//! map, and cookie jar. Cloning a `Client` is a refcount bump and shares the
//! same pool, so the idiomatic pattern is *obtain once, clone freely*.
//!
//! # Why there is no ambient global client
//!
//! Many HTTP stacks expose a free function — `reqwest::get(url)`, a process
//! `static DEFAULT_CLIENT`, a thread-local — that performs network I/O without
//! the caller ever proving they were granted I/O authority. That is **ambient
//! authority**: any code path (a `Drop` impl, a panic handler, a deep library
//! call) can reach the network invisibly, which is exactly what Asupersync's
//! capability model forbids (see the no-ambient-authority invariant in
//! `AGENTS.md`).
//!
//! Asupersync therefore provides **no** free `Client::get()` and **no** global
//! `Client` singleton. The only way to obtain the default client is
//! [`Client::default_for_runtime`], which **demands a [`Cx`] whose capability
//! set carries I/O authority** (`Caps: HasIo`). The capability flows through
//! the `Cx` parameter and is checked by the compiler at the call site — a
//! context that cannot prove I/O authority cannot even name the accessor.
//!
//! This mirrors how every other effect is reached in this runtime: timers
//! through [`Cx::timer_driver`](crate::Cx::timer_driver), raw I/O through
//! [`Cx::io`](crate::Cx::io). The HTTP client is just another capability-gated
//! handle.
//!
//! # Example
//!
//! ```
//! use asupersync::Cx;
//! use asupersync::http::{Client, ClientError};
//!
//! async fn fetch(cx: &Cx) -> Result<u16, ClientError> {
//! // Obtain the runtime's default pooled client. You must hand it a `Cx`
//! // that carries I/O authority — there is no ambient `Client::get()`.
//! let client = Client::default_for_runtime(cx);
//! let resp = client.get("http://example.invalid/").send(cx).await?;
//! Ok(resp.status)
//! }
//! # let _ = fetch; // documents the call shape; not executed (no network here)
//! ```
//!
//! # Capability proof (compile-fail)
//!
//! A context carrying *no* capabilities does not satisfy `Caps: HasIo`, so the
//! accessor is unreachable from it — there is no ambient escape hatch:
//!
//! ```compile_fail
//! use asupersync::Cx;
//! use asupersync::cx::NoCaps;
//! use asupersync::http::Client;
//!
//! // ERROR[E0277]: the trait bound `NoCaps: HasIo` is not satisfied.
//! fn requires_io(cx: &Cx<NoCaps>) {
//! let _ = Client::default_for_runtime(cx);
//! }
//! # let _ = requires_io;
//! ```
use crateCx;
use crateHasIo;
/// Cheap-clone handle over a shared HTTP connection pool.
///
/// `Client` is the public, ergonomic alias for
/// [`HttpClient`](super::h1::HttpClient). See the [module docs](self) for the
/// no-ambient-global philosophy and the capability-gated
/// [`Client::default_for_runtime`] accessor.
pub use HttpClient as Client;