ntex_h2/config.rs
1#![allow(
2 clippy::new_without_default,
3 clippy::cast_sign_loss,
4 clippy::cast_precision_loss,
5 clippy::missing_panics_doc
6)]
7use std::{cell::Cell, time::Duration};
8
9use ntex_service::cfg::{CfgContext, Configuration};
10use ntex_util::time::Seconds;
11
12use crate::{consts, frame, frame::Settings, frame::WindowSize};
13
14#[derive(Debug)]
15/// Http2 connection configuration
16pub struct ServiceConfig {
17 pub(crate) settings: Settings,
18 /// Initial window size of locally initiated streams
19 pub(crate) window_sz: i32,
20 pub(crate) window_sz_threshold: WindowSize,
21 /// How long a locally reset stream should ignore frames
22 pub(crate) reset_duration: Duration,
23 /// Maximum number of locally reset streams to keep at a time
24 pub(crate) reset_max: usize,
25 /// Initial window size for new connections.
26 pub(crate) connection_window_sz: i32,
27 pub(crate) connection_window_sz_threshold: WindowSize,
28 /// Maximum number of remote initiated streams
29 pub(crate) remote_max_concurrent_streams: Option<u32>,
30 /// Limit number of continuation frames for headers
31 pub(crate) max_header_continuations: usize,
32 /// Maximum number of headers
33 pub(crate) max_headers: usize,
34 /// Capacity availability timeout
35 pub(crate) capacity_timeout: Option<Seconds>,
36 // /// If extended connect protocol is enabled.
37 // pub extended_connect_protocol_enabled: bool,
38 /// Connection timeouts
39 pub(crate) handshake_timeout: Seconds,
40 pub(crate) ping_timeout: Seconds,
41
42 config: CfgContext,
43}
44
45impl Default for ServiceConfig {
46 fn default() -> Self {
47 ServiceConfig::new()
48 }
49}
50
51impl Configuration for ServiceConfig {
52 const NAME: &str = "Http/2 service configuration";
53
54 fn ctx(&self) -> &CfgContext {
55 &self.config
56 }
57
58 fn set_ctx(&mut self, ctx: CfgContext) {
59 self.config = ctx;
60 }
61}
62
63impl ServiceConfig {
64 /// Create configuration
65 pub fn new() -> Self {
66 let window_sz = frame::DEFAULT_INITIAL_WINDOW_SIZE;
67 let window_sz_threshold = ((frame::DEFAULT_INITIAL_WINDOW_SIZE as f32) / 3.0) as u32;
68 let connection_window_sz = consts::DEFAULT_CONNECTION_WINDOW_SIZE;
69 let connection_window_sz_threshold =
70 ((consts::DEFAULT_CONNECTION_WINDOW_SIZE as f32) / 4.0) as u32;
71
72 let mut settings = Settings::default();
73 settings.set_max_concurrent_streams(Some(256));
74 settings.set_enable_push(false);
75 settings.set_max_header_list_size(Some(consts::DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE));
76
77 ServiceConfig {
78 window_sz,
79 window_sz_threshold,
80 connection_window_sz,
81 connection_window_sz_threshold,
82 settings,
83 reset_max: consts::DEFAULT_RESET_STREAM_MAX,
84 reset_duration: consts::DEFAULT_RESET_STREAM_SECS.into(),
85 remote_max_concurrent_streams: Some(consts::DEFAULT_MAX_CONCURRENT_STREAMS),
86 max_headers: consts::DEFAULT_MAX_HEADERS,
87 max_header_continuations: consts::DEFAULT_MAX_COUNTINUATIONS,
88 capacity_timeout: Some(consts::DEFAULT_CAPACITY_TIMEOUT),
89 handshake_timeout: Seconds(5),
90 ping_timeout: Seconds(10),
91 config: CfgContext::default(),
92 }
93 }
94}
95
96impl ServiceConfig {
97 #[must_use]
98 /// Indicates the initial window size (in octets) for stream-level
99 /// flow control for received data.
100 ///
101 /// The initial window of a stream is used as part of flow control. For more
102 /// details, see [`FlowControl`].
103 ///
104 /// The default value is 65,535.
105 pub fn set_initial_window_size(mut self, size: i32) -> Self {
106 assert!((0..=consts::MAX_WINDOW_SIZE).contains(&size));
107
108 self.window_sz = size;
109 self.window_sz_threshold = ((size as f32) / 3.0) as u32;
110 self.settings.set_initial_window_size(Some(size as u32));
111 self
112 }
113
114 #[must_use]
115 #[allow(clippy::missing_panics_doc)]
116 /// Indicates the initial window size (in octets) for connection-level flow control
117 /// for received data.
118 ///
119 /// The initial window of a connection is used as part of flow control. For more details,
120 /// see [`FlowControl`].
121 ///
122 /// The default value is 1Mb.
123 ///
124 /// [`FlowControl`]: ../struct.FlowControl.html
125 pub fn set_initial_connection_window_size(mut self, size: i32) -> Self {
126 assert!((0..=consts::MAX_WINDOW_SIZE).contains(&size));
127 self.connection_window_sz = size;
128 self.connection_window_sz_threshold = ((size as f32) / 4.0) as u32;
129 self
130 }
131
132 #[must_use]
133 /// Indicates the size (in octets) of the largest HTTP/2 frame payload that the
134 /// configured server is able to accept.
135 ///
136 /// The sender may send data frames that are **smaller** than this value,
137 /// but any data larger than `max` will be broken up into multiple `DATA`
138 /// frames.
139 ///
140 /// The value **must** be between 16,384 and 16,777,215. The default value is 16,384.
141 ///
142 /// # Panics
143 ///
144 /// This function panics if `max` is not within the legal range specified
145 /// above.
146 pub fn set_max_frame_size(mut self, max: u32) -> Self {
147 self.settings.set_max_frame_size(max);
148 self
149 }
150
151 #[must_use]
152 /// Set the maximum number of headers.
153 ///
154 /// When a request is received, the parser will reserve a buffer
155 /// to store headers for optimal performance.
156 ///
157 /// If server receives more headers than the buffer size, it resets
158 /// stream with `REFUSED_STREAM` reason.
159 ///
160 /// Default is set to 96
161 pub fn set_max_headers(mut self, val: usize) -> Self {
162 self.max_headers = val;
163 self
164 }
165
166 #[must_use]
167 /// Sets the max size of received header frames.
168 ///
169 /// This advisory setting informs a peer of the maximum size of header list
170 /// that the sender is prepared to accept, in octets. The value is based on
171 /// the uncompressed size of header fields, including the length of the name
172 /// and value in octets plus an overhead of 32 octets for each header field.
173 ///
174 /// This setting is also used to limit the maximum amount of data that is
175 /// buffered to decode HEADERS frames.
176 ///
177 /// By default value is set to 48Kb.
178 pub fn set_max_header_list_size(mut self, max: u32) -> Self {
179 self.settings.set_max_header_list_size(Some(max));
180 self
181 }
182
183 #[must_use]
184 /// Sets the max number of continuation frames for HEADERS
185 ///
186 /// By default value is set to 5
187 pub fn set_max_header_continuation_frames(mut self, max: usize) -> Self {
188 self.max_header_continuations = max;
189 self
190 }
191
192 #[must_use]
193 /// Sets the maximum number of concurrent streams.
194 ///
195 /// The maximum concurrent streams setting only controls the maximum number
196 /// of streams that can be initiated by the remote peer. In other words,
197 /// when this setting is set to 100, this does not limit the number of
198 /// concurrent streams that can be created by the caller.
199 ///
200 /// It is recommended that this value be no smaller than 100, so as to not
201 /// unnecessarily limit parallelism. However, any value is legal, including
202 /// 0. If `max` is set to 0, then the remote will not be permitted to
203 /// initiate streams.
204 ///
205 /// Note that streams in the reserved state, i.e., push promises that have
206 /// been reserved but the stream has not started, do not count against this
207 /// setting.
208 ///
209 /// Also note that if the remote *does* exceed the value set here, it is not
210 /// a protocol level error. Instead, the `ntex-h2` library will immediately reset
211 /// the stream.
212 ///
213 /// See [Section 5.1.2] in the HTTP/2 spec for more details.
214 ///
215 /// [Section 5.1.2]: https://http2.github.io/http2-spec/#rfc.section.5.1.2
216 pub fn set_max_concurrent_streams(mut self, max: u32) -> Self {
217 self.remote_max_concurrent_streams = Some(max);
218 self.settings.set_max_concurrent_streams(Some(max));
219 self
220 }
221
222 #[must_use]
223 /// Sets the maximum number of concurrent locally reset streams.
224 ///
225 /// When a stream is explicitly reset by either calling
226 /// [`SendResponse::send_reset`] or by dropping a [`SendResponse`] instance
227 /// before completing the stream, the HTTP/2 specification requires that
228 /// any further frames received for that stream must be ignored for "some
229 /// time".
230 ///
231 /// In order to satisfy the specification, internal state must be maintained
232 /// to implement the behavior. This state grows linearly with the number of
233 /// streams that are locally reset.
234 ///
235 /// The `max_concurrent_reset_streams` setting configures sets an upper
236 /// bound on the amount of state that is maintained. When this max value is
237 /// reached, the oldest reset stream is purged from memory.
238 ///
239 /// Once the stream has been fully purged from memory, any additional frames
240 /// received for that stream will result in a connection level protocol
241 /// error, forcing the connection to terminate.
242 ///
243 /// The default value is 32.
244 pub fn set_max_concurrent_reset_streams(mut self, val: usize) -> Self {
245 self.reset_max = val;
246 self
247 }
248
249 #[must_use]
250 /// Sets the maximum number of concurrent locally reset streams.
251 ///
252 /// When a stream is explicitly reset by either calling
253 /// [`SendResponse::send_reset`] or by dropping a [`SendResponse`] instance
254 /// before completing the stream, the HTTP/2 specification requires that
255 /// any further frames received for that stream must be ignored for "some
256 /// time".
257 ///
258 /// In order to satisfy the specification, internal state must be maintained
259 /// to implement the behavior. This state grows linearly with the number of
260 /// streams that are locally reset.
261 ///
262 /// The `reset_stream_duration` setting configures the max amount of time
263 /// this state will be maintained in memory. Once the duration elapses, the
264 /// stream state is purged from memory.
265 ///
266 /// Once the stream has been fully purged from memory, any additional frames
267 /// received for that stream will result in a connection level protocol
268 /// error, forcing the connection to terminate.
269 ///
270 /// The default value is 30 seconds.
271 pub fn set_reset_stream_duration(mut self, dur: Seconds) -> Self {
272 self.reset_duration = dur.into();
273 self
274 }
275
276 // /// Enables the [extended CONNECT protocol].
277 // ///
278 // /// [extended CONNECT protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
279 // pub fn enable_connect_protocol(&self) -> &Self {
280 // let mut s = self.0.settings.get();
281 // s.set_enable_connect_protocol(Some(1));
282 // self.0.settings.set(s);
283 // self
284 // }
285
286 #[must_use]
287 /// Set handshake timeout.
288 ///
289 /// Hadnshake includes receiving preface and completing connection preparation.
290 ///
291 /// By default handshake timeuot is 5 seconds.
292 pub fn set_handshake_timeout(mut self, timeout: Seconds) -> Self {
293 self.handshake_timeout = timeout;
294 self
295 }
296
297 #[must_use]
298 /// Set ping timeout.
299 ///
300 /// By default ping time-out is set to 60 seconds.
301 pub fn set_ping_timeout(mut self, timeout: Seconds) -> Self {
302 self.ping_timeout = timeout;
303 self
304 }
305
306 #[must_use]
307 /// Set capacity availability timeout.
308 ///
309 /// By default time-out is set to 3 seconds.
310 pub fn set_capacity_timeout(mut self, timeout: Seconds) -> Self {
311 if timeout.is_zero() {
312 self.capacity_timeout = None;
313 } else {
314 self.capacity_timeout = Some(timeout);
315 }
316 self
317 }
318}
319
320thread_local! {
321 static SHUTDOWN: Cell<bool> = const { Cell::new(false) };
322}
323
324// Current limitation, shutdown is thread global
325impl ServiceConfig {
326 /// Check if service is shutting down.
327 pub fn is_shutdown(&self) -> bool {
328 SHUTDOWN.with(Cell::get)
329 }
330
331 /// Set service shutdown.
332 pub fn shutdown() {
333 SHUTDOWN.with(|v| v.set(true));
334 }
335}