Skip to main content

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}