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