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}