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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! HTTP/2 path for Bun's fetch HTTP client.
//!
//! `ClientSession` owns the TLS socket once ALPN selects "h2" and is the
//! `ActiveSocket` variant the HTTPContext handlers dispatch to. It holds the
//! connection-scoped state — HPACK tables, write/read buffers, server
//! SETTINGS — and a map of active `Stream`s, each bound to one `HTTPClient`.
//! Response frames are parsed into per-stream buffers and then handed to the
//! same `picohttp.Response` / `handleResponseBody` machinery the HTTP/1.1
//! path uses, so redirects, decompression and the result callback are shared.
use AtomicI32;
/// Advertised as SETTINGS_INITIAL_WINDOW_SIZE; replenished via WINDOW_UPDATE
/// once half has been consumed.
// PORT NOTE: Zig type was `u31` (HTTP/2 window sizes are 31-bit); Rust has no
// `u31`, so widen to `u32`. Value `1 << 24` is well within range.
pub const LOCAL_INITIAL_WINDOW_SIZE: u32 = 1 << 24;
/// Advertised as SETTINGS_MAX_HEADER_LIST_SIZE and enforced as a hard cap on
/// both the wire header block (HEADERS + CONTINUATION accumulation) and the
/// decoded header list, so a CONTINUATION flood or HPACK-amplification bomb
/// can't OOM the process. RFC 9113 §6.5.2 makes the setting advisory, so the
/// cap is checked locally regardless of what the server honors.
pub const LOCAL_MAX_HEADER_LIST_SIZE: u32 = 256 * 1024;
/// CONTINUATION frames allowed per header block. Matches nghttp2's
/// `NGHTTP2_DEFAULT_MAX_CONTINUATIONS` (CVE-2024-28182).
pub const LOCAL_MAX_CONTINUATIONS: u8 = 8;
/// HPACK dynamic-table size used when neither side negotiates otherwise
/// (RFC 7541 §4.3: `INITIAL_DYNAMIC_TABLE_SIZE` = 4096). This is both the
/// decoder cap when the preface advertises no SETTINGS_HEADER_TABLE_SIZE and
/// the encoder cap until the server's SETTINGS raises it
/// (`pending_hpack_enc_capacity`).
pub const DEFAULT_HPACK_TABLE_SIZE: u32 = 4096;
/// The SETTINGS_HEADER_TABLE_SIZE value carried in a client-preface SETTINGS
/// payload (wire form: `u16` setting id BE + `u32` value BE per 6-byte unit),
/// i.e. the maximum dynamic-table size this endpoint told the peer its
/// encoder may use (RFC 9113 §6.5.2). RFC 7541 §4.2 then entitles the peer to
/// signal any table size up to this value with a Dynamic Table Size Update,
/// so the connection's HPACK decoder must be capped at exactly this value —
/// not at [`DEFAULT_HPACK_TABLE_SIZE`].
///
/// Returns `None` when the payload is absent or carries no 0x0001 unit
/// (the 4096 default applies). When several 0x0001 units are present the
/// last wins, mirroring in-order SETTINGS processing by the peer.
/// `write_buffer` high-water mark. `writeDataWindowed` stops queueing once the
/// userland send buffer crosses this even if flow-control window remains, so a
/// large grant doesn't duplicate the whole body in memory before the first
/// `flush()`. `onWritable → drainSendBodies` resumes once the socket drains.
pub const WRITE_BUFFER_HIGH_WATER: usize = 256 * 1024;
/// Abandon the connection (ENHANCE_YOUR_CALM) if queued control-frame replies
/// (PING/SETTINGS ACKs) push `write_buffer` past this while the socket is
/// stalled — caps the PING-reflection growth at a fixed budget instead of OOM.
pub const WRITE_BUFFER_CONTROL_LIMIT: usize = 1024 * 1024;
/// Live-object counters for the leak test in fetch-http2-leak.test.ts.
/// Incremented at allocation, decremented in deinit. Read from the JS thread
/// via TestingAPIs.liveCounts so they must be atomic.
// PORT NOTE: Zig names are `live_sessions`/`live_streams` (snake_case module
// vars). Kept verbatim so cross-crate readers (`bun_http_jsc`) and the gated
// submodules see the same identifier the Zig uses; SCREAMING_SNAKE aliases
// preserved for the existing internal references.
pub static live_sessions: AtomicI32 = new;
pub static live_streams: AtomicI32 = new;
pub use live_sessions as LIVE_SESSIONS;
pub use live_streams as LIVE_STREAMS;
// Un-gated: Stream/ClientSession/dispatch/encode now compile against the
// real crate surface (bridge stubs below cover gated HTTPClient methods).
// They no longer reference bun_str/bun_output/crate::state/crate::Signal.
pub use ClientSession;
pub use SessionPtr;
pub use PendingConnect;
pub use Stream;
// PORT NOTE: Zig had `pub const TestingAPIs = @import("../http_jsc/headers_jsc.zig").H2TestingAPIs;`
// — a `*_jsc` alias. Deleted per PORTING.md: `to_js`/host-fn surfaces live in the
// `*_jsc` crate via extension traits; the base crate has no mention of jsc.
// ═══════════════════════════════════════════════════════════════════════
// Thin `h2_*` forwarders on HTTPClient / HTTPContext that the h2_client
// modules call. The real bodies live in lib.rs
// (`register_abort_tracker` … `progress_update`) and HTTPContext.rs
// (`register_h2` / `unregister_h2`); these now monomorphize the const-generic
// `<IS_SSL>` callees to `<true>` (HTTP/2 is TLS-only) and erase the
// `picohttp::Request<'_>` borrow back to `'static` so ClientSession can keep
// using `client` after building the request. Kept as inherent methods so the
// many call sites in `h2_client/*.rs` need no churn.
// ═══════════════════════════════════════════════════════════════════════
pub
// ported from: src/http/H2Client.zig